Commit 3587ea78 authored by Alexander Basov's avatar Alexander Basov
Browse files

Done --> Feature #25432

reCAPTCHA implementation
parent 7d921ffc
......@@ -16,19 +16,10 @@
package org.genesys2.server.servlet.controller;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.genesys2.server.model.UserRole;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.security.lockout.AccountLockoutManager;
import org.genesys2.server.service.ContentService;
import org.genesys2.server.service.CropService;
import org.genesys2.server.service.EMailVerificationService;
import org.genesys2.server.service.OrganizationService;
import org.genesys2.server.service.StatisticsService;
import org.genesys2.server.service.UserService;
import org.genesys2.server.service.*;
import org.genesys2.server.servlet.filter.LocaleURLFilter;
import org.genesys2.util.ReCaptchaUtil;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -45,6 +36,10 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Locale;
/**
* Controller which simply handles *.html requests
*/
......@@ -75,6 +70,9 @@ public class HtmlController extends BaseController {
@Value("${captcha.publicKey}")
private String captchaPublicKey;
@Value("${captcha.siteKey}")
private String captchaSiteKey;
@Autowired
private StatisticsService statisticsService;
......@@ -121,20 +119,19 @@ public class HtmlController extends BaseController {
@RequestMapping(value = "registration")
public String registration(ModelMap model) {
model.addAttribute("captchaPublicKey", captchaPublicKey);
model.addAttribute("captchaSiteKey", captchaSiteKey);
model.addAttribute("blurp", contentService.getGlobalArticle("registration", getLocale()));
return "/registration";
}
@RequestMapping(value = "new-user")
public String addUser(@ModelAttribute User user, BindingResult bindingResult, HttpServletRequest req,
@RequestParam(value = "recaptcha_challenge_field", required = false) String challenge,
@RequestParam(value = "recaptcha_response_field", required = false) String response) {
@RequestParam(value = "g-recaptcha-response", required = false) String response) throws IOException {
user.getRoles().add(UserRole.USER);
validator.validate(user, bindingResult);
// Validate the reCAPTCHA
if (!ReCaptchaUtil.isValid(captchaPrivateKey, req.getRemoteAddr(), challenge, response)) {
if (!ReCaptchaUtil.isValid(response)) {
_logger.warn("Invalid captcha.");
final FieldError fieldError = new FieldError("comment", "captcha", response, false, new String[] { "errors.badCaptcha" }, null, "Please try again.");
bindingResult.addError(fieldError);
......
......@@ -16,10 +16,6 @@
package org.genesys2.server.servlet.controller;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.ContentService;
......@@ -43,6 +39,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Controller to manage request processing.
*
......@@ -103,7 +104,7 @@ public class RequestController extends BaseController {
public String submit(ModelMap model, HttpServletRequest req, @RequestParam(value = "email", defaultValue = "", required = true) String requestEmail,
@RequestParam(value = "recaptcha_challenge_field", required = false) String challenge,
@RequestParam(value = "recaptcha_response_field", required = false) String response, @RequestParam(value = "notes") String notes,
@RequestParam(value = "purpose") int purposeType, @RequestParam(value = "smta", required = false) Boolean preacceptSMTA) {
@RequestParam(value = "purpose") int purposeType, @RequestParam(value = "smta", required = false) Boolean preacceptSMTA) throws IOException {
final User currentUser = SecurityContextUtil.getCurrentUser();
model.addAttribute("notes", notes);
......@@ -118,7 +119,7 @@ public class RequestController extends BaseController {
}
// Validate the reCAPTCHA
if (currentUser == null && !ReCaptchaUtil.isValid(captchaPrivateKey, req.getRemoteAddr(), challenge, response)) {
if (currentUser == null && !ReCaptchaUtil.isValid(response)) {
_logger.warn("Recaptcha not valid. Stopping here.");
return start(model);
}
......
/**
* Copyright 2014 Global Crop Diversity Trust
*
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
......@@ -16,15 +16,18 @@
package org.genesys2.util;
import java.net.InetAddress;
import java.net.UnknownHostException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.tanesha.recaptcha.ReCaptcha;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.tanesha.recaptcha.ReCaptcha;
import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* {@link ReCaptcha} wrapper
......@@ -33,32 +36,48 @@ import net.tanesha.recaptcha.ReCaptchaResponse;
*
*/
public class ReCaptchaUtil {
private final static Log LOG = LogFactory.getLog(ReCaptchaUtil.class);
private static final String HTTPS_GOOGLE_SERVER = "https://www.google.com/recaptcha/api";
// Validate the reCAPTCHA
public static boolean isValid(String captchaPrivateKey, String remoteAddr, String challenge, String response) {
boolean isLocalRequest = false;
private final static Log LOG = LogFactory.getLog(ReCaptchaUtil.class);
private static final String URL = "https://www.google.com/recaptcha/api/siteverify";
private static final String captchaSecretKey = "6LfQ1g4TAAAAALNIQ_5tRbzEErflan1qdS9wAHXR";
public static boolean isValid(String reCaptchaResponse) throws IOException {
if (reCaptchaResponse == null || "".equals(reCaptchaResponse)) {
return false;
}
URL url = new URL(URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// add reuqest header
connection.setRequestMethod("POST");
String postParams = "secret=" + captchaSecretKey + "&response=" + reCaptchaResponse;
// Send post request
connection.setDoOutput(true);
DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
dataOutputStream.writeBytes(postParams);
dataOutputStream.flush();
dataOutputStream.close();
try {
final InetAddress remoteInetAddr = InetAddress.getByName(remoteAddr);
isLocalRequest = remoteInetAddr.isLinkLocalAddress() || remoteInetAddr.isAnyLocalAddress() || remoteInetAddr.isLoopbackAddress();
LOG.warn("Remote addr: " + remoteAddr + " " + remoteInetAddr + " isLocal=" + isLocalRequest);
} catch (final UnknownHostException e1) {
LOG.warn(e1.getMessage());
}
int responseCode = connection.getResponseCode();
LOG.info("Send recaptcha post request to --> " + url + "\nPost parameters : " + postParams
+ "\n Response Code : " + responseCode);
if (isLocalRequest) {
LOG.info("Ignoring localhost re-captcha.");
return true;
}
BufferedReader in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
final ReCaptchaImpl reCaptcha = new ReCaptchaImpl();
reCaptcha.setRecaptchaServer(HTTPS_GOOGLE_SERVER);
reCaptcha.setPrivateKey(captchaPrivateKey);
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
final ReCaptchaResponse reCaptchaResponse = reCaptcha.checkAnswer(remoteAddr, challenge, response);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(response.toString());
return reCaptchaResponse.isValid();
}
return jsonNode.findValue("success").asBoolean();
}
}
<%-- Bad, bad... Injecting scripts all over the HTTPS --%>
<script type="text/javascript" src="https://www.google.com/recaptcha/api/challenge?k=${captchaPublicKey}"></script>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="${captchaSiteKey}"></div>
<noscript>
<iframe src="http://www.google.com/recaptcha/api/noscript?k=${captchaPublicKey}" height="300" width="500" frameborder="0"></iframe><br>
<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input type="hidden" name="recaptcha_response_field" value="manual_challenge">
<div style="width: 302px; height: 422px;">
<div style="width: 302px; height: 422px; position: relative;">
<div style="width: 302px; height: 422px; position: absolute;">
<iframe src="https://www.google.com/recaptcha/api/fallback?k=${captchaSiteKey}"
frameborder="0" scrolling="no"
style="width: 302px; height:422px; border-style: none;">
</iframe>
</div>
<div style="width: 300px; height: 60px; border-style: none;
bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px;
background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px;">
<textarea id="g-recaptcha-response" name="g-recaptcha-response"
class="g-recaptcha-response"
style="width: 250px; height: 40px; border: 1px solid #c1c1c1;
margin: 10px 25px; padding: 0px; resize: none;" >
</textarea>
</div>
</div>
</div>
</noscript>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment