From b5e4a0dc28966479e0f2d5c0d69f2f38f734fee1 Mon Sep 17 00:00:00 2001
From: Leonardo Vannucci <leonardo.vannucci@grupposistematica.it>
Date: Thu, 09 Aug 2018 17:35:28 +0200
Subject: [PATCH] Implementazione recaptcha

---
 dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/RestTemplateConfig.java            |   32 ++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/service/Utils.java                           |   60 ++++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponse.java             |  103 ++++++++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/config/dg1cloud.properties                   |    7 
 dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaController.java |   44 +++
 dg1cloud-core/pom.xml                                                                        |   30 ++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ValidReCaptcha.java                |   25 ++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaService.java              |   46 +++
 dg1cloud-core/src/main/resources/templates/downloadFileCaptcha.html                          |   48 ++++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadFileController.java       |   70 +----
 dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadPageController.java       |   38 +++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/CaptchaSettings.java               |   46 +++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaConstraintValidator.java  |   28 ++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaForm.java       |   41 +++
 dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponseFilter.java       |   75 ++++++
 15 files changed, 634 insertions(+), 59 deletions(-)

diff --git a/dg1cloud-core/pom.xml b/dg1cloud-core/pom.xml
index 1fcf8c8..d97e3cd 100644
--- a/dg1cloud-core/pom.xml
+++ b/dg1cloud-core/pom.xml
@@ -53,6 +53,22 @@
 		</dependency>
 		
 		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-thymeleaf</artifactId>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-configuration-processor</artifactId>
+			<optional>true</optional>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpclient</artifactId>
+		</dependency>
+		
+		<dependency>
 			<groupId>org.postgresql</groupId>
 			<artifactId>postgresql</artifactId>
 		</dependency>
@@ -77,7 +93,19 @@
 		<dependency>
 			<groupId>commons-codec</groupId>
 			<artifactId>commons-codec</artifactId>
-			</dependency>
+		</dependency>
+		
+		<!-- bootstrap and jquery -->
+		<dependency>
+			<groupId>org.webjars</groupId>
+			<artifactId>bootstrap</artifactId>
+			<version>3.3.7</version>
+		</dependency>
+		<dependency>
+			<groupId>org.webjars</groupId>
+			<artifactId>jquery</artifactId>
+			<version>3.2.1</version>
+		</dependency>
 		
 	</dependencies>
 
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/config/dg1cloud.properties b/dg1cloud-core/src/main/java/it/digione/dg1cloud/config/dg1cloud.properties
index f74bf52..79d523e 100644
--- a/dg1cloud-core/src/main/java/it/digione/dg1cloud/config/dg1cloud.properties
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/config/dg1cloud.properties
@@ -13,4 +13,9 @@
 
 server.servlet.context-path=/dg1cloud
 
-external.server.address.base.url=http://vannux.grupposistematica.it:8080/dg1cloud
\ No newline at end of file
+external.server.address.base.url=http://vannux.grupposistematica.it:8080/dg1cloud
+
+google.recaptcha.enabled=false
+google.recaptcha.url=https://www.google.com/recaptcha/api/siteverify
+google.recaptcha.key=6LfCHmkUAAAAAFa1KmVtPFecItBriBHx1qmKHcVS
+google.recaptcha.secret=6LfCHmkUAAAAAKXdXWO36daHnItKQwkEvXOpX6YY
\ No newline at end of file
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadFileController.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadFileController.java
index 4962f61..be67cd2 100644
--- a/dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadFileController.java
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadFileController.java
@@ -1,82 +1,40 @@
 package it.digione.dg1cloud.controller;
 
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 
-import javax.servlet.ServletContext;
-
-import org.apache.logging.log4j.util.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.InputStreamResource;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 
-import it.digione.dg1cloud.model.RegDocument;
-import it.digione.dg1cloud.repository.RegDocumentRepository;
-import it.digione.dg1cloud.service.CloudService;
+import it.digione.dg1cloud.recaptcha.CaptchaSettings;
+import it.digione.dg1cloud.service.Utils;
 
 @Controller
 public class DownloadFileController {
 	
-	private static final Logger logger = LoggerFactory.getLogger(CloudService.class);
+	private static final Logger logger = LoggerFactory.getLogger(DownloadFileController.class);
 	
-	@Autowired private ServletContext servletContext;
-	@Autowired private RegDocumentRepository regDocumentRepository;
+	@Autowired private Utils utils;
+	@Autowired CaptchaSettings captchaSettings;
 	
 	@RequestMapping("/downloadFile")
 	public ResponseEntity<InputStreamResource> downloadFile(@RequestParam String fileName,
-															@RequestParam long id,
-															@RequestParam(value = "secretKey", required = false) String secretKey) throws IOException {
+											@RequestParam long id,
+											@RequestParam(value = "secretKey", required = false) String secretKey) throws IOException {
 		
-		logger.debug("Avvio download file {} con id {}, fileName, id");
-		
-		MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
-		try {
-			mediaType = MediaType.parseMediaType(servletContext.getMimeType(fileName));
-		} catch (Exception e) {
-			logger.warn("Errore nello stabilire il mime type del file. VerrĂ  usato " + mediaType.toString(), e);
+		if ( captchaSettings.isEnabled() == true ) {
+			String error = "Download diretto non ammesso. Usare la pagina di download tramite reCaptcha";
+			logger.error(error);
+			throw new RuntimeException("Download diretto non ammesso. Usare la pagina di download tramite reCaptcha");
 		}
 		
-		RegDocument regDocument = regDocumentRepository.getOne(id);
-		
-		if (regDocument.getFileName().equalsIgnoreCase(fileName) == false ) {
-			logger.error("Il nome del file richiesto non corrisponde con quello referenziato dall'id");
-			throw new RuntimeException("Il nome del file richiesto non corrisponde con quello referenziato dall'id");
-		}
-		
-		if ( regDocument.getSecretKey() != null ) {
-			logger.debug("E' stata specificata una secretKey. Avvio le verifiche.");
-			if ( Strings.isEmpty(secretKey) == true ) {
-				logger.error("Non e' stata inviata la secretKey");
-				throw new RuntimeException("Per scaricare il file occorre specificare la secretKey");
-			} else {
-				logger.debug("Controllo corrispondenza della secretKey");
-				if (secretKey.equals(regDocument.getSecretKey()) == false ) {
-					logger.error("La secretKey inviata non corrisponde a quella impostata in fase di richiesta salvataggio del file");
-					throw new RuntimeException("La secretKey inviata non corrisponde a quella impostata in fase di richiesta salvataggio del file");
-				} else {
-					logger.debug("SecretKey verificata correttamente");
-				}
-			}
-		}
-		
-		File file = new File(regDocument.getFilePath());
-		FileInputStream fis = new FileInputStream(file);
-		InputStreamResource isr = new InputStreamResource(fis);
-		return ResponseEntity.ok()
-				// Content-Disposition
-				.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
-				// Content-Type
-				.contentType(mediaType)
-				// Contet-Length
-				.contentLength(file.length()) //
-				.body(isr);
+		return utils.getDownloadResponseEntity(fileName, id, secretKey);
 	}
+
+	
 }
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadPageController.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadPageController.java
new file mode 100644
index 0000000..8ce7a97
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadPageController.java
@@ -0,0 +1,38 @@
+package it.digione.dg1cloud.controller;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.ModelAndView;
+
+import it.digione.dg1cloud.recaptcha.CaptchaSettings;
+import it.digione.dg1cloud.recaptcha.DownloadFileCaptchaForm;
+
+@Controller
+public class DownloadPageController {
+	
+	private static final Logger logger = LoggerFactory.getLogger(DownloadPageController.class);
+	
+	@RequestMapping("/downloadPage")
+	public ModelAndView downloadPage(@RequestParam String fileName, @RequestParam long id,
+								@RequestParam(value = "secretKey", required = false) String secretKey) throws IOException {
+		
+		logger.debug("Preparazione pagina download file {} con id {}", fileName, id);
+		
+		DownloadFileCaptchaForm downloadParams = new DownloadFileCaptchaForm();
+		
+		downloadParams.setFileName(fileName);
+		downloadParams.setId(id);
+		downloadParams.setSecretKey(secretKey);
+		
+		ModelAndView modelAndView = new ModelAndView("downloadFileCaptcha");
+		modelAndView.addObject("downloadFileCaptchaForm", downloadParams);
+		
+		return modelAndView;
+	}
+}
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/CaptchaSettings.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/CaptchaSettings.java
new file mode 100644
index 0000000..f52a4f8
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/CaptchaSettings.java
@@ -0,0 +1,46 @@
+package it.digione.dg1cloud.recaptcha;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "google.recaptcha")
+public class CaptchaSettings {
+
+	private boolean enabled;
+	private String url;
+	private String key;
+	private String secret;
+
+	public boolean isEnabled() {
+		return enabled;
+	}
+
+	public void setEnabled(boolean enabled) {
+		this.enabled = enabled;
+	}
+
+	public String getUrl() {
+		return url;
+	}
+
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+	public String getKey() {
+		return key;
+	}
+
+	public void setKey(String key) {
+		this.key = key;
+	}
+
+	public String getSecret() {
+		return secret;
+	}
+
+	public void setSecret(String secret) {
+		this.secret = secret;
+	}
+}
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaController.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaController.java
new file mode 100644
index 0000000..1a6c79f
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaController.java
@@ -0,0 +1,44 @@
+package it.digione.dg1cloud.recaptcha;
+
+import java.io.FileNotFoundException;
+
+import javax.validation.Valid;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+import it.digione.dg1cloud.service.Utils;
+
+@Controller
+@RequestMapping("/downloadFileCaptcha")
+public class DownloadFileCaptchaController {
+	
+	@Autowired private Utils utils;
+	
+	@ModelAttribute("downloadFileCaptchaForm")
+	public DownloadFileCaptchaForm downloadFileCaptchaForm() {
+		return new DownloadFileCaptchaForm();
+	}
+
+	@PostMapping
+	public Object handleDownloadFileCaptcha(@ModelAttribute("downloadFileCaptchaForm") @Valid DownloadFileCaptchaForm form,
+									BindingResult result) throws FileNotFoundException{
+		
+		ModelAndView modelAndView;
+		if (result.hasErrors()){
+			modelAndView = new ModelAndView("downloadFileCaptcha");
+			modelAndView.addObject(result);
+			modelAndView.addObject("downloadFileCaptchaForm", form);
+			
+			return modelAndView;
+		} else {
+			return utils.getDownloadResponseEntity(form.getFileName(), form.getId(), form.getSecretKey());
+		}
+		
+	}
+}
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaForm.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaForm.java
new file mode 100644
index 0000000..d743d43
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaForm.java
@@ -0,0 +1,41 @@
+package it.digione.dg1cloud.recaptcha;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+public class DownloadFileCaptchaForm {
+
+	@NotNull
+	private Long id;
+	private String fileName;
+	private String secretKey;
+	@NotEmpty
+	@ValidReCaptcha
+	private String reCaptchaResponse;
+	
+	public Long getId() {
+		return id;
+	}
+	public void setId(Long id) {
+		this.id = id;
+	}
+	public String getFileName() {
+		return fileName;
+	}
+	public void setFileName(String fileName) {
+		this.fileName = fileName;
+	}
+	public String getSecretKey() {
+		return secretKey;
+	}
+	public void setSecretKey(String secretKey) {
+		this.secretKey = secretKey;
+	}
+	public String getReCaptchaResponse() {
+		return reCaptchaResponse;
+	}
+	public void setReCaptchaResponse(String reCaptchaResponse) {
+		this.reCaptchaResponse = reCaptchaResponse;
+	}
+
+}
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaConstraintValidator.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaConstraintValidator.java
new file mode 100644
index 0000000..1833ffc
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaConstraintValidator.java
@@ -0,0 +1,28 @@
+package it.digione.dg1cloud.recaptcha;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public class ReCaptchaConstraintValidator implements ConstraintValidator<ValidReCaptcha, String> {
+
+	@Autowired
+	private ReCaptchaService reCaptchaService;
+
+	@Override
+	public void initialize(ValidReCaptcha constraintAnnotation) {
+
+	}
+
+	@Override
+	public boolean isValid(String reCaptchaResponse, ConstraintValidatorContext context) {
+
+		if (reCaptchaResponse == null || reCaptchaResponse.isEmpty()){
+			return true;
+		}
+
+		return reCaptchaService.validate(reCaptchaResponse);
+	}
+
+}
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponse.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponse.java
new file mode 100644
index 0000000..ddef69f
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponse.java
@@ -0,0 +1,103 @@
+package it.digione.dg1cloud.recaptcha;
+
+import com.fasterxml.jackson.annotation.*;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonPropertyOrder({
+		"success",
+		"challenge_ts",
+		"hostname",
+		"error-codes"
+})
+public class ReCaptchaResponse {
+
+	@JsonProperty("success")
+	private boolean success;
+
+	@JsonProperty("challenge_ts")
+	private Date challengeTs;
+
+	@JsonProperty("hostname")
+	private String hostname;
+
+	@JsonProperty("error-codes")
+	private ErrorCode[] errorCodes;
+
+	@JsonIgnore
+	public boolean hasClientError() {
+		ErrorCode[] errors = getErrorCodes();
+		if(errors == null) {
+			return false;
+		}
+		for(ErrorCode error : errors) {
+			switch(error) {
+				case InvalidResponse:
+				case MissingResponse:
+					return true;
+			case InvalidSecret:
+				break;
+			case MissingSecret:
+				break;
+			default:
+				break;
+			}
+		}
+		return false;
+	}
+
+	static enum ErrorCode {
+		MissingSecret,	 InvalidSecret,
+		MissingResponse,   InvalidResponse;
+
+		private static Map<String, ErrorCode> errorsMap = new HashMap<>(4);
+
+		static {
+			errorsMap.put("missing-input-secret",   MissingSecret);
+			errorsMap.put("invalid-input-secret",   InvalidSecret);
+			errorsMap.put("missing-input-response", MissingResponse);
+			errorsMap.put("invalid-input-response", InvalidResponse);
+		}
+
+		@JsonCreator
+		public static ErrorCode forValue(String value) {
+			return errorsMap.get(value.toLowerCase());
+		}
+	}
+
+	public boolean isSuccess() {
+		return success;
+	}
+
+	public void setSuccess(boolean success) {
+		this.success = success;
+	}
+
+	public Date getChallengeTs() {
+		return challengeTs;
+	}
+
+	public void setChallengeTs(Date challengeTs) {
+		this.challengeTs = challengeTs;
+	}
+
+	public String getHostname() {
+		return hostname;
+	}
+
+	public void setHostname(String hostname) {
+		this.hostname = hostname;
+	}
+
+	public ErrorCode[] getErrorCodes() {
+		return errorCodes;
+	}
+
+	public void setErrorCodes(ErrorCode[] errorCodes) {
+		this.errorCodes = errorCodes;
+	}
+}
\ No newline at end of file
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponseFilter.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponseFilter.java
new file mode 100644
index 0000000..727acee
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponseFilter.java
@@ -0,0 +1,75 @@
+package it.digione.dg1cloud.recaptcha;
+
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class ReCaptchaResponseFilter implements Filter {
+
+	private static final String RE_CAPTCHA_ALIAS = "reCaptchaResponse";
+	private static final String RE_CAPTCHA_RESPONSE = "g-recaptcha-response";
+
+	@Override
+	public void init(FilterConfig filterConfig) throws ServletException {
+	}
+
+	@Override
+	public void doFilter(ServletRequest servletRequest,
+						 ServletResponse servletResponse,
+						 FilterChain chain) throws IOException, ServletException {
+
+		HttpServletRequest request = (HttpServletRequest) servletRequest;
+		HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+		if (request.getParameter(RE_CAPTCHA_RESPONSE) != null) {
+			ReCaptchaHttpServletRequest reCaptchaRequest = new ReCaptchaHttpServletRequest(request);
+			chain.doFilter(reCaptchaRequest, response);
+		} else {
+			chain.doFilter(request, response);
+		}
+	}
+
+	@Override
+	public void destroy() {
+	}
+
+	private static class ReCaptchaHttpServletRequest extends HttpServletRequestWrapper {
+
+		final Map<String, String[]> params;
+
+		ReCaptchaHttpServletRequest(HttpServletRequest request) {
+			super(request);
+			params = new HashMap<>(request.getParameterMap());
+			params.put(RE_CAPTCHA_ALIAS, request.getParameterValues(RE_CAPTCHA_RESPONSE));
+		}
+
+		@Override
+		public String getParameter(String name) {
+			return params.containsKey(name) ? params.get(name)[0] : null;
+		}
+
+		@Override
+		public Map<String, String[]> getParameterMap() {
+			return params;
+		}
+
+		@Override
+		public Enumeration<String> getParameterNames() {
+			return Collections.enumeration(params.keySet());
+		}
+
+		@Override
+		public String[] getParameterValues(String name) {
+			return params.get(name);
+		}
+	}
+}
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaService.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaService.java
new file mode 100644
index 0000000..aee72ee
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaService.java
@@ -0,0 +1,46 @@
+package it.digione.dg1cloud.recaptcha;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestOperations;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.URI;
+
+@Service
+public class ReCaptchaService {
+
+	private static final Logger log = LoggerFactory.getLogger(ReCaptchaService.class);
+
+	@Autowired
+	private RestOperations restTemplate;
+
+	@Autowired
+	private CaptchaSettings captchaSettings;
+
+	@Autowired
+	private HttpServletRequest request;
+
+	public boolean validate(String reCaptchaResponse){
+		URI verifyUri = URI.create(String.format(
+				captchaSettings.getUrl() + "?secret=%s&response=%s&remoteip=%s",
+				captchaSettings.getSecret(),
+				reCaptchaResponse,
+				request.getRemoteAddr()
+		));
+
+		try {
+			ReCaptchaResponse response = restTemplate.getForObject(verifyUri, ReCaptchaResponse.class);
+			return response.isSuccess();
+		} catch (Exception ignored){
+			log.error("", ignored);
+			// ignore when google services are not available
+			// maybe add some sort of logging or trigger that'll alert the administrator
+		}
+
+		return true;
+	}
+
+}
\ No newline at end of file
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/RestTemplateConfig.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/RestTemplateConfig.java
new file mode 100644
index 0000000..69c4173
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/RestTemplateConfig.java
@@ -0,0 +1,32 @@
+package it.digione.dg1cloud.recaptcha;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class RestTemplateConfig {
+
+	@Bean
+	public RestTemplate restTemplate(ClientHttpRequestFactory httpRequestFactory) {
+		RestTemplate template = new RestTemplate(httpRequestFactory);
+		template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
+		return template;
+	}
+
+	@Bean
+	public ClientHttpRequestFactory httpRequestFactory(HttpClient httpClient) {
+		return new HttpComponentsClientHttpRequestFactory(httpClient);
+	}
+
+	@Bean
+	public HttpClient httpClient() {
+		return HttpClientBuilder.create().build();
+	}
+
+}
\ No newline at end of file
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ValidReCaptcha.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ValidReCaptcha.java
new file mode 100644
index 0000000..ba8f107
--- /dev/null
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ValidReCaptcha.java
@@ -0,0 +1,25 @@
+package it.digione.dg1cloud.recaptcha;
+
+import javax.validation.Payload;
+import javax.validation.Constraint;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Documented
+@Constraint(validatedBy = ReCaptchaConstraintValidator.class)
+@Target({ TYPE, FIELD, ANNOTATION_TYPE })
+@Retention(RUNTIME)
+public @interface ValidReCaptcha {
+	
+	String message() default "Invalid ReCaptcha";
+
+	Class<?>[] groups() default {};
+
+	Class<? extends Payload>[] payload() default {};
+
+}
\ No newline at end of file
diff --git a/dg1cloud-core/src/main/java/it/digione/dg1cloud/service/Utils.java b/dg1cloud-core/src/main/java/it/digione/dg1cloud/service/Utils.java
index 815499b..4c54b5d 100644
--- a/dg1cloud-core/src/main/java/it/digione/dg1cloud/service/Utils.java
+++ b/dg1cloud-core/src/main/java/it/digione/dg1cloud/service/Utils.java
@@ -1,33 +1,44 @@
 package it.digione.dg1cloud.service;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+
+import javax.servlet.ServletContext;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.logging.log4j.util.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 import org.springframework.web.util.UriBuilder;
 import org.springframework.web.util.UriComponentsBuilder;
 
 import it.digione.dg1cloud.config.AppConfig;
 import it.digione.dg1cloud.model.RegDocument;
+import it.digione.dg1cloud.repository.RegDocumentRepository;
 
 @Service("utils")
 public class Utils {
 	
 	@Autowired private AppConfig appConfig;
+	@Autowired private RegDocumentRepository regDocumentRepository;
+	@Autowired private ServletContext servletContext;
 	
 	private static final Logger logger = LoggerFactory.getLogger(CloudService.class);
 	
 	public URL generateUrl(RegDocument regDocument) throws MalformedURLException {
 		logger.debug("Genero l'url per il record file {} con id {}", regDocument.getFileName(), regDocument.getDocumentId());
 		UriBuilder uriBuilder = UriComponentsBuilder.fromUriString(appConfig.getExternalBaseUrl());
-		uriBuilder.path("/downloadFile");
+		uriBuilder.path("/downloadPage");
 		uriBuilder.queryParam("fileName", regDocument.getFileName());
 		uriBuilder.queryParam("id", regDocument.getDocumentId());
 		if ( Strings.isEmpty(regDocument.getSecretKey()) == false ) {
@@ -53,4 +64,51 @@
 			logger.debug("La directory {} non e' vuota", directory.getAbsolutePath());
 		}
 	}
+	
+	public ResponseEntity<InputStreamResource> getDownloadResponseEntity(String fileName, long id, String secretKey)
+			throws FileNotFoundException {
+		logger.debug("Avvio download file {} con id {}", fileName, id);
+		
+		MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
+		try {
+			mediaType = MediaType.parseMediaType(servletContext.getMimeType(fileName));
+		} catch (Exception e) {
+			logger.warn("Errore nello stabilire il mime type del file. VerrĂ  usato " + mediaType.toString(), e);
+		}
+		
+		RegDocument regDocument = regDocumentRepository.getOne(id);
+		
+		if (regDocument.getFileName().equalsIgnoreCase(fileName) == false ) {
+			logger.error("Il nome del file richiesto non corrisponde con quello referenziato dall'id");
+			throw new RuntimeException("Il nome del file richiesto non corrisponde con quello referenziato dall'id");
+		}
+		
+		if ( regDocument.getSecretKey() != null ) {
+			logger.debug("E' stata specificata una secretKey. Avvio le verifiche.");
+			if ( Strings.isEmpty(secretKey) == true ) {
+				logger.error("Non e' stata inviata la secretKey");
+				throw new RuntimeException("Per scaricare il file occorre specificare la secretKey");
+			} else {
+				logger.debug("Controllo corrispondenza della secretKey");
+				if (secretKey.equals(regDocument.getSecretKey()) == false ) {
+					logger.error("La secretKey inviata non corrisponde a quella impostata in fase di richiesta salvataggio del file");
+					throw new RuntimeException("La secretKey inviata non corrisponde a quella impostata in fase di richiesta salvataggio del file");
+				} else {
+					logger.debug("SecretKey verificata correttamente");
+				}
+			}
+		}
+		
+		File file = new File(regDocument.getFilePath());
+		FileInputStream fis = new FileInputStream(file);
+		InputStreamResource isr = new InputStreamResource(fis);
+		return ResponseEntity.ok()
+				// Content-Disposition
+				.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
+				// Content-Type
+				.contentType(mediaType)
+				// Contet-Length
+				.contentLength(file.length()) //
+				.body(isr);
+	}
 }
diff --git a/dg1cloud-core/src/main/resources/templates/downloadFileCaptcha.html b/dg1cloud-core/src/main/resources/templates/downloadFileCaptcha.html
new file mode 100644
index 0000000..79218f2
--- /dev/null
+++ b/dg1cloud-core/src/main/resources/templates/downloadFileCaptcha.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org">
+	<head>
+		<meta charset="utf-8"/>
+		<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
+		<meta name="viewport" content="width=device-width, initial-scale=1"/>
+		
+		<link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.min.css}"/>
+		<link rel="stylesheet" type="text/css" th:href="@{/css/main.css}"/>
+		
+		<title>Link download file</title>
+		<script src='https://www.google.com/recaptcha/api.js'></script>
+	</head>
+	<body>
+		<div class="container">
+			<div class="row">
+				<div class="col-md-4 col-md-offset-4">
+					<div class="panel panel-default">
+						<div class="panel-body">
+							<div class="text-center">
+								<h3><i class="glyphicon glyphicon-lock" style="font-size:2em;"></i></h3>
+								<h2 class="text-center" th:text="*{downloadFileCaptchaForm.fileName}"></h2>
+								<div class="panel-body">
+									<form action="#" th:action="@{/downloadFileCaptcha}" th:object="${downloadFileCaptchaForm}" method="post">
+										<input type="hidden" th:field="*{id}" />
+										<input type="hidden" th:field="*{fileName}" />
+										<input type="hidden" th:field="*{secretKey}" />
+										<div class="form-group">
+											<div class="g-recaptcha" th:attr="data-sitekey=${@captchaSettings.getKey()}"></div>
+											<p class="error-message"
+												th:each="error: ${#fields.errors('reCaptchaResponse')}"
+												th:text="${error}">Errore validazione reCaptcha</p>
+										</div>
+										<div class="form-group">
+											<button name="downloadFile" type="submit" th:text="'Scarica il file ' + *{fileName} "></button>
+										</div>
+									</form>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+		<script type="text/javascript" th:src="@{/webjars/jquery/3.2.1/jquery.min.js/}"></script>
+		<script type="text/javascript" th:src="@{/webjars/bootstrap/3.3.7/js/bootstrap.min.js}"></script>
+	</body>
+</html>
\ No newline at end of file

--
Gitblit v1.6.2