Leonardo Vannucci
2018-08-09 b5e4a0dc28966479e0f2d5c0d69f2f38f734fee1
Implementazione recaptcha
11 files added
4 files modified
693 ■■■■■ changed files
dg1cloud-core/pom.xml 30 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/config/dg1cloud.properties 7 ●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadFileController.java 70 ●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadPageController.java 38 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/CaptchaSettings.java 46 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaController.java 44 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaForm.java 41 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaConstraintValidator.java 28 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponse.java 103 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponseFilter.java 75 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaService.java 46 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/RestTemplateConfig.java 32 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ValidReCaptcha.java 25 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/java/it/digione/dg1cloud/service/Utils.java 60 ●●●●● patch | view | raw | blame | history
dg1cloud-core/src/main/resources/templates/downloadFileCaptcha.html 48 ●●●●● patch | view | raw | blame | history
dg1cloud-core/pom.xml
....@@ -53,6 +53,22 @@
5353 </dependency>
5454
5555 <dependency>
56
+ <groupId>org.springframework.boot</groupId>
57
+ <artifactId>spring-boot-starter-thymeleaf</artifactId>
58
+ </dependency>
59
+
60
+ <dependency>
61
+ <groupId>org.springframework.boot</groupId>
62
+ <artifactId>spring-boot-configuration-processor</artifactId>
63
+ <optional>true</optional>
64
+ </dependency>
65
+
66
+ <dependency>
67
+ <groupId>org.apache.httpcomponents</groupId>
68
+ <artifactId>httpclient</artifactId>
69
+ </dependency>
70
+
71
+ <dependency>
5672 <groupId>org.postgresql</groupId>
5773 <artifactId>postgresql</artifactId>
5874 </dependency>
....@@ -77,7 +93,19 @@
7793 <dependency>
7894 <groupId>commons-codec</groupId>
7995 <artifactId>commons-codec</artifactId>
80
- </dependency>
96
+ </dependency>
97
+
98
+ <!-- bootstrap and jquery -->
99
+ <dependency>
100
+ <groupId>org.webjars</groupId>
101
+ <artifactId>bootstrap</artifactId>
102
+ <version>3.3.7</version>
103
+ </dependency>
104
+ <dependency>
105
+ <groupId>org.webjars</groupId>
106
+ <artifactId>jquery</artifactId>
107
+ <version>3.2.1</version>
108
+ </dependency>
81109
82110 </dependencies>
83111
dg1cloud-core/src/main/java/it/digione/dg1cloud/config/dg1cloud.properties
....@@ -13,4 +13,9 @@
1313
1414 server.servlet.context-path=/dg1cloud
1515
16
-external.server.address.base.url=http://vannux.grupposistematica.it:8080/dg1cloud
16
+external.server.address.base.url=http://vannux.grupposistematica.it:8080/dg1cloud
17
+
18
+google.recaptcha.enabled=false
19
+google.recaptcha.url=https://www.google.com/recaptcha/api/siteverify
20
+google.recaptcha.key=6LfCHmkUAAAAAFa1KmVtPFecItBriBHx1qmKHcVS
21
+google.recaptcha.secret=6LfCHmkUAAAAAKXdXWO36daHnItKQwkEvXOpX6YY
dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadFileController.java
....@@ -1,82 +1,40 @@
11 package it.digione.dg1cloud.controller;
22
3
-import java.io.File;
4
-import java.io.FileInputStream;
53 import java.io.IOException;
64
7
-import javax.servlet.ServletContext;
8
-
9
-import org.apache.logging.log4j.util.Strings;
105 import org.slf4j.Logger;
116 import org.slf4j.LoggerFactory;
127 import org.springframework.beans.factory.annotation.Autowired;
138 import org.springframework.core.io.InputStreamResource;
14
-import org.springframework.http.HttpHeaders;
15
-import org.springframework.http.MediaType;
169 import org.springframework.http.ResponseEntity;
1710 import org.springframework.stereotype.Controller;
1811 import org.springframework.web.bind.annotation.RequestMapping;
1912 import org.springframework.web.bind.annotation.RequestParam;
2013
21
-import it.digione.dg1cloud.model.RegDocument;
22
-import it.digione.dg1cloud.repository.RegDocumentRepository;
23
-import it.digione.dg1cloud.service.CloudService;
14
+import it.digione.dg1cloud.recaptcha.CaptchaSettings;
15
+import it.digione.dg1cloud.service.Utils;
2416
2517 @Controller
2618 public class DownloadFileController {
2719
28
- private static final Logger logger = LoggerFactory.getLogger(CloudService.class);
20
+ private static final Logger logger = LoggerFactory.getLogger(DownloadFileController.class);
2921
30
- @Autowired private ServletContext servletContext;
31
- @Autowired private RegDocumentRepository regDocumentRepository;
22
+ @Autowired private Utils utils;
23
+ @Autowired CaptchaSettings captchaSettings;
3224
3325 @RequestMapping("/downloadFile")
3426 public ResponseEntity<InputStreamResource> downloadFile(@RequestParam String fileName,
35
- @RequestParam long id,
36
- @RequestParam(value = "secretKey", required = false) String secretKey) throws IOException {
27
+ @RequestParam long id,
28
+ @RequestParam(value = "secretKey", required = false) String secretKey) throws IOException {
3729
38
- logger.debug("Avvio download file {} con id {}, fileName, id");
39
-
40
- MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
41
- try {
42
- mediaType = MediaType.parseMediaType(servletContext.getMimeType(fileName));
43
- } catch (Exception e) {
44
- logger.warn("Errore nello stabilire il mime type del file. VerrĂ  usato " + mediaType.toString(), e);
30
+ if ( captchaSettings.isEnabled() == true ) {
31
+ String error = "Download diretto non ammesso. Usare la pagina di download tramite reCaptcha";
32
+ logger.error(error);
33
+ throw new RuntimeException("Download diretto non ammesso. Usare la pagina di download tramite reCaptcha");
4534 }
4635
47
- RegDocument regDocument = regDocumentRepository.getOne(id);
48
-
49
- if (regDocument.getFileName().equalsIgnoreCase(fileName) == false ) {
50
- logger.error("Il nome del file richiesto non corrisponde con quello referenziato dall'id");
51
- throw new RuntimeException("Il nome del file richiesto non corrisponde con quello referenziato dall'id");
52
- }
53
-
54
- if ( regDocument.getSecretKey() != null ) {
55
- logger.debug("E' stata specificata una secretKey. Avvio le verifiche.");
56
- if ( Strings.isEmpty(secretKey) == true ) {
57
- logger.error("Non e' stata inviata la secretKey");
58
- throw new RuntimeException("Per scaricare il file occorre specificare la secretKey");
59
- } else {
60
- logger.debug("Controllo corrispondenza della secretKey");
61
- if (secretKey.equals(regDocument.getSecretKey()) == false ) {
62
- logger.error("La secretKey inviata non corrisponde a quella impostata in fase di richiesta salvataggio del file");
63
- throw new RuntimeException("La secretKey inviata non corrisponde a quella impostata in fase di richiesta salvataggio del file");
64
- } else {
65
- logger.debug("SecretKey verificata correttamente");
66
- }
67
- }
68
- }
69
-
70
- File file = new File(regDocument.getFilePath());
71
- FileInputStream fis = new FileInputStream(file);
72
- InputStreamResource isr = new InputStreamResource(fis);
73
- return ResponseEntity.ok()
74
- // Content-Disposition
75
- .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
76
- // Content-Type
77
- .contentType(mediaType)
78
- // Contet-Length
79
- .contentLength(file.length()) //
80
- .body(isr);
36
+ return utils.getDownloadResponseEntity(fileName, id, secretKey);
8137 }
38
+
39
+
8240 }
dg1cloud-core/src/main/java/it/digione/dg1cloud/controller/DownloadPageController.java
....@@ -0,0 +1,38 @@
1
+package it.digione.dg1cloud.controller;
2
+
3
+import java.io.IOException;
4
+
5
+import org.slf4j.Logger;
6
+import org.slf4j.LoggerFactory;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.stereotype.Controller;
9
+import org.springframework.web.bind.annotation.RequestMapping;
10
+import org.springframework.web.bind.annotation.RequestParam;
11
+import org.springframework.web.servlet.ModelAndView;
12
+
13
+import it.digione.dg1cloud.recaptcha.CaptchaSettings;
14
+import it.digione.dg1cloud.recaptcha.DownloadFileCaptchaForm;
15
+
16
+@Controller
17
+public class DownloadPageController {
18
+
19
+ private static final Logger logger = LoggerFactory.getLogger(DownloadPageController.class);
20
+
21
+ @RequestMapping("/downloadPage")
22
+ public ModelAndView downloadPage(@RequestParam String fileName, @RequestParam long id,
23
+ @RequestParam(value = "secretKey", required = false) String secretKey) throws IOException {
24
+
25
+ logger.debug("Preparazione pagina download file {} con id {}", fileName, id);
26
+
27
+ DownloadFileCaptchaForm downloadParams = new DownloadFileCaptchaForm();
28
+
29
+ downloadParams.setFileName(fileName);
30
+ downloadParams.setId(id);
31
+ downloadParams.setSecretKey(secretKey);
32
+
33
+ ModelAndView modelAndView = new ModelAndView("downloadFileCaptcha");
34
+ modelAndView.addObject("downloadFileCaptchaForm", downloadParams);
35
+
36
+ return modelAndView;
37
+ }
38
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/CaptchaSettings.java
....@@ -0,0 +1,46 @@
1
+package it.digione.dg1cloud.recaptcha;
2
+
3
+import org.springframework.boot.context.properties.ConfigurationProperties;
4
+import org.springframework.stereotype.Component;
5
+
6
+@Component
7
+@ConfigurationProperties(prefix = "google.recaptcha")
8
+public class CaptchaSettings {
9
+
10
+ private boolean enabled;
11
+ private String url;
12
+ private String key;
13
+ private String secret;
14
+
15
+ public boolean isEnabled() {
16
+ return enabled;
17
+ }
18
+
19
+ public void setEnabled(boolean enabled) {
20
+ this.enabled = enabled;
21
+ }
22
+
23
+ public String getUrl() {
24
+ return url;
25
+ }
26
+
27
+ public void setUrl(String url) {
28
+ this.url = url;
29
+ }
30
+
31
+ public String getKey() {
32
+ return key;
33
+ }
34
+
35
+ public void setKey(String key) {
36
+ this.key = key;
37
+ }
38
+
39
+ public String getSecret() {
40
+ return secret;
41
+ }
42
+
43
+ public void setSecret(String secret) {
44
+ this.secret = secret;
45
+ }
46
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaController.java
....@@ -0,0 +1,44 @@
1
+package it.digione.dg1cloud.recaptcha;
2
+
3
+import java.io.FileNotFoundException;
4
+
5
+import javax.validation.Valid;
6
+
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.stereotype.Controller;
9
+import org.springframework.validation.BindingResult;
10
+import org.springframework.web.bind.annotation.ModelAttribute;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.RequestMapping;
13
+import org.springframework.web.servlet.ModelAndView;
14
+
15
+import it.digione.dg1cloud.service.Utils;
16
+
17
+@Controller
18
+@RequestMapping("/downloadFileCaptcha")
19
+public class DownloadFileCaptchaController {
20
+
21
+ @Autowired private Utils utils;
22
+
23
+ @ModelAttribute("downloadFileCaptchaForm")
24
+ public DownloadFileCaptchaForm downloadFileCaptchaForm() {
25
+ return new DownloadFileCaptchaForm();
26
+ }
27
+
28
+ @PostMapping
29
+ public Object handleDownloadFileCaptcha(@ModelAttribute("downloadFileCaptchaForm") @Valid DownloadFileCaptchaForm form,
30
+ BindingResult result) throws FileNotFoundException{
31
+
32
+ ModelAndView modelAndView;
33
+ if (result.hasErrors()){
34
+ modelAndView = new ModelAndView("downloadFileCaptcha");
35
+ modelAndView.addObject(result);
36
+ modelAndView.addObject("downloadFileCaptchaForm", form);
37
+
38
+ return modelAndView;
39
+ } else {
40
+ return utils.getDownloadResponseEntity(form.getFileName(), form.getId(), form.getSecretKey());
41
+ }
42
+
43
+ }
44
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/DownloadFileCaptchaForm.java
....@@ -0,0 +1,41 @@
1
+package it.digione.dg1cloud.recaptcha;
2
+
3
+import javax.validation.constraints.NotEmpty;
4
+import javax.validation.constraints.NotNull;
5
+
6
+public class DownloadFileCaptchaForm {
7
+
8
+ @NotNull
9
+ private Long id;
10
+ private String fileName;
11
+ private String secretKey;
12
+ @NotEmpty
13
+ @ValidReCaptcha
14
+ private String reCaptchaResponse;
15
+
16
+ public Long getId() {
17
+ return id;
18
+ }
19
+ public void setId(Long id) {
20
+ this.id = id;
21
+ }
22
+ public String getFileName() {
23
+ return fileName;
24
+ }
25
+ public void setFileName(String fileName) {
26
+ this.fileName = fileName;
27
+ }
28
+ public String getSecretKey() {
29
+ return secretKey;
30
+ }
31
+ public void setSecretKey(String secretKey) {
32
+ this.secretKey = secretKey;
33
+ }
34
+ public String getReCaptchaResponse() {
35
+ return reCaptchaResponse;
36
+ }
37
+ public void setReCaptchaResponse(String reCaptchaResponse) {
38
+ this.reCaptchaResponse = reCaptchaResponse;
39
+ }
40
+
41
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaConstraintValidator.java
....@@ -0,0 +1,28 @@
1
+package it.digione.dg1cloud.recaptcha;
2
+
3
+import org.springframework.beans.factory.annotation.Autowired;
4
+
5
+import javax.validation.ConstraintValidator;
6
+import javax.validation.ConstraintValidatorContext;
7
+
8
+public class ReCaptchaConstraintValidator implements ConstraintValidator<ValidReCaptcha, String> {
9
+
10
+ @Autowired
11
+ private ReCaptchaService reCaptchaService;
12
+
13
+ @Override
14
+ public void initialize(ValidReCaptcha constraintAnnotation) {
15
+
16
+ }
17
+
18
+ @Override
19
+ public boolean isValid(String reCaptchaResponse, ConstraintValidatorContext context) {
20
+
21
+ if (reCaptchaResponse == null || reCaptchaResponse.isEmpty()){
22
+ return true;
23
+ }
24
+
25
+ return reCaptchaService.validate(reCaptchaResponse);
26
+ }
27
+
28
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponse.java
....@@ -0,0 +1,103 @@
1
+package it.digione.dg1cloud.recaptcha;
2
+
3
+import com.fasterxml.jackson.annotation.*;
4
+
5
+import java.util.Date;
6
+import java.util.HashMap;
7
+import java.util.Map;
8
+
9
+@JsonInclude(JsonInclude.Include.NON_NULL)
10
+@JsonIgnoreProperties(ignoreUnknown = true)
11
+@JsonPropertyOrder({
12
+ "success",
13
+ "challenge_ts",
14
+ "hostname",
15
+ "error-codes"
16
+})
17
+public class ReCaptchaResponse {
18
+
19
+ @JsonProperty("success")
20
+ private boolean success;
21
+
22
+ @JsonProperty("challenge_ts")
23
+ private Date challengeTs;
24
+
25
+ @JsonProperty("hostname")
26
+ private String hostname;
27
+
28
+ @JsonProperty("error-codes")
29
+ private ErrorCode[] errorCodes;
30
+
31
+ @JsonIgnore
32
+ public boolean hasClientError() {
33
+ ErrorCode[] errors = getErrorCodes();
34
+ if(errors == null) {
35
+ return false;
36
+ }
37
+ for(ErrorCode error : errors) {
38
+ switch(error) {
39
+ case InvalidResponse:
40
+ case MissingResponse:
41
+ return true;
42
+ case InvalidSecret:
43
+ break;
44
+ case MissingSecret:
45
+ break;
46
+ default:
47
+ break;
48
+ }
49
+ }
50
+ return false;
51
+ }
52
+
53
+ static enum ErrorCode {
54
+ MissingSecret, InvalidSecret,
55
+ MissingResponse, InvalidResponse;
56
+
57
+ private static Map<String, ErrorCode> errorsMap = new HashMap<>(4);
58
+
59
+ static {
60
+ errorsMap.put("missing-input-secret", MissingSecret);
61
+ errorsMap.put("invalid-input-secret", InvalidSecret);
62
+ errorsMap.put("missing-input-response", MissingResponse);
63
+ errorsMap.put("invalid-input-response", InvalidResponse);
64
+ }
65
+
66
+ @JsonCreator
67
+ public static ErrorCode forValue(String value) {
68
+ return errorsMap.get(value.toLowerCase());
69
+ }
70
+ }
71
+
72
+ public boolean isSuccess() {
73
+ return success;
74
+ }
75
+
76
+ public void setSuccess(boolean success) {
77
+ this.success = success;
78
+ }
79
+
80
+ public Date getChallengeTs() {
81
+ return challengeTs;
82
+ }
83
+
84
+ public void setChallengeTs(Date challengeTs) {
85
+ this.challengeTs = challengeTs;
86
+ }
87
+
88
+ public String getHostname() {
89
+ return hostname;
90
+ }
91
+
92
+ public void setHostname(String hostname) {
93
+ this.hostname = hostname;
94
+ }
95
+
96
+ public ErrorCode[] getErrorCodes() {
97
+ return errorCodes;
98
+ }
99
+
100
+ public void setErrorCodes(ErrorCode[] errorCodes) {
101
+ this.errorCodes = errorCodes;
102
+ }
103
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaResponseFilter.java
....@@ -0,0 +1,75 @@
1
+package it.digione.dg1cloud.recaptcha;
2
+
3
+import org.springframework.stereotype.Component;
4
+
5
+import javax.servlet.*;
6
+import javax.servlet.http.HttpServletRequest;
7
+import javax.servlet.http.HttpServletRequestWrapper;
8
+import javax.servlet.http.HttpServletResponse;
9
+import java.io.IOException;
10
+import java.util.Collections;
11
+import java.util.Enumeration;
12
+import java.util.HashMap;
13
+import java.util.Map;
14
+
15
+@Component
16
+public class ReCaptchaResponseFilter implements Filter {
17
+
18
+ private static final String RE_CAPTCHA_ALIAS = "reCaptchaResponse";
19
+ private static final String RE_CAPTCHA_RESPONSE = "g-recaptcha-response";
20
+
21
+ @Override
22
+ public void init(FilterConfig filterConfig) throws ServletException {
23
+ }
24
+
25
+ @Override
26
+ public void doFilter(ServletRequest servletRequest,
27
+ ServletResponse servletResponse,
28
+ FilterChain chain) throws IOException, ServletException {
29
+
30
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
31
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
32
+
33
+ if (request.getParameter(RE_CAPTCHA_RESPONSE) != null) {
34
+ ReCaptchaHttpServletRequest reCaptchaRequest = new ReCaptchaHttpServletRequest(request);
35
+ chain.doFilter(reCaptchaRequest, response);
36
+ } else {
37
+ chain.doFilter(request, response);
38
+ }
39
+ }
40
+
41
+ @Override
42
+ public void destroy() {
43
+ }
44
+
45
+ private static class ReCaptchaHttpServletRequest extends HttpServletRequestWrapper {
46
+
47
+ final Map<String, String[]> params;
48
+
49
+ ReCaptchaHttpServletRequest(HttpServletRequest request) {
50
+ super(request);
51
+ params = new HashMap<>(request.getParameterMap());
52
+ params.put(RE_CAPTCHA_ALIAS, request.getParameterValues(RE_CAPTCHA_RESPONSE));
53
+ }
54
+
55
+ @Override
56
+ public String getParameter(String name) {
57
+ return params.containsKey(name) ? params.get(name)[0] : null;
58
+ }
59
+
60
+ @Override
61
+ public Map<String, String[]> getParameterMap() {
62
+ return params;
63
+ }
64
+
65
+ @Override
66
+ public Enumeration<String> getParameterNames() {
67
+ return Collections.enumeration(params.keySet());
68
+ }
69
+
70
+ @Override
71
+ public String[] getParameterValues(String name) {
72
+ return params.get(name);
73
+ }
74
+ }
75
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ReCaptchaService.java
....@@ -0,0 +1,46 @@
1
+package it.digione.dg1cloud.recaptcha;
2
+
3
+import org.slf4j.Logger;
4
+import org.slf4j.LoggerFactory;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.stereotype.Service;
7
+import org.springframework.web.client.RestOperations;
8
+
9
+import javax.servlet.http.HttpServletRequest;
10
+import java.net.URI;
11
+
12
+@Service
13
+public class ReCaptchaService {
14
+
15
+ private static final Logger log = LoggerFactory.getLogger(ReCaptchaService.class);
16
+
17
+ @Autowired
18
+ private RestOperations restTemplate;
19
+
20
+ @Autowired
21
+ private CaptchaSettings captchaSettings;
22
+
23
+ @Autowired
24
+ private HttpServletRequest request;
25
+
26
+ public boolean validate(String reCaptchaResponse){
27
+ URI verifyUri = URI.create(String.format(
28
+ captchaSettings.getUrl() + "?secret=%s&response=%s&remoteip=%s",
29
+ captchaSettings.getSecret(),
30
+ reCaptchaResponse,
31
+ request.getRemoteAddr()
32
+ ));
33
+
34
+ try {
35
+ ReCaptchaResponse response = restTemplate.getForObject(verifyUri, ReCaptchaResponse.class);
36
+ return response.isSuccess();
37
+ } catch (Exception ignored){
38
+ log.error("", ignored);
39
+ // ignore when google services are not available
40
+ // maybe add some sort of logging or trigger that'll alert the administrator
41
+ }
42
+
43
+ return true;
44
+ }
45
+
46
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/RestTemplateConfig.java
....@@ -0,0 +1,32 @@
1
+package it.digione.dg1cloud.recaptcha;
2
+
3
+import org.apache.http.client.HttpClient;
4
+import org.apache.http.impl.client.HttpClientBuilder;
5
+import org.springframework.context.annotation.Bean;
6
+import org.springframework.context.annotation.Configuration;
7
+import org.springframework.http.client.ClientHttpRequestFactory;
8
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
9
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
10
+import org.springframework.web.client.RestTemplate;
11
+
12
+@Configuration
13
+public class RestTemplateConfig {
14
+
15
+ @Bean
16
+ public RestTemplate restTemplate(ClientHttpRequestFactory httpRequestFactory) {
17
+ RestTemplate template = new RestTemplate(httpRequestFactory);
18
+ template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
19
+ return template;
20
+ }
21
+
22
+ @Bean
23
+ public ClientHttpRequestFactory httpRequestFactory(HttpClient httpClient) {
24
+ return new HttpComponentsClientHttpRequestFactory(httpClient);
25
+ }
26
+
27
+ @Bean
28
+ public HttpClient httpClient() {
29
+ return HttpClientBuilder.create().build();
30
+ }
31
+
32
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/recaptcha/ValidReCaptcha.java
....@@ -0,0 +1,25 @@
1
+package it.digione.dg1cloud.recaptcha;
2
+
3
+import javax.validation.Payload;
4
+import javax.validation.Constraint;
5
+import java.lang.annotation.Documented;
6
+import java.lang.annotation.Retention;
7
+import java.lang.annotation.Target;
8
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
9
+import static java.lang.annotation.ElementType.TYPE;
10
+import static java.lang.annotation.ElementType.FIELD;
11
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
12
+
13
+@Documented
14
+@Constraint(validatedBy = ReCaptchaConstraintValidator.class)
15
+@Target({ TYPE, FIELD, ANNOTATION_TYPE })
16
+@Retention(RUNTIME)
17
+public @interface ValidReCaptcha {
18
+
19
+ String message() default "Invalid ReCaptcha";
20
+
21
+ Class<?>[] groups() default {};
22
+
23
+ Class<? extends Payload>[] payload() default {};
24
+
25
+}
dg1cloud-core/src/main/java/it/digione/dg1cloud/service/Utils.java
....@@ -1,33 +1,44 @@
11 package it.digione.dg1cloud.service;
22
33 import java.io.File;
4
+import java.io.FileInputStream;
5
+import java.io.FileNotFoundException;
46 import java.io.IOException;
57 import java.net.MalformedURLException;
68 import java.net.URL;
9
+
10
+import javax.servlet.ServletContext;
711
812 import org.apache.commons.io.FileUtils;
913 import org.apache.logging.log4j.util.Strings;
1014 import org.slf4j.Logger;
1115 import org.slf4j.LoggerFactory;
1216 import org.springframework.beans.factory.annotation.Autowired;
17
+import org.springframework.core.io.InputStreamResource;
18
+import org.springframework.http.HttpHeaders;
19
+import org.springframework.http.MediaType;
20
+import org.springframework.http.ResponseEntity;
1321 import org.springframework.stereotype.Service;
1422 import org.springframework.web.util.UriBuilder;
1523 import org.springframework.web.util.UriComponentsBuilder;
1624
1725 import it.digione.dg1cloud.config.AppConfig;
1826 import it.digione.dg1cloud.model.RegDocument;
27
+import it.digione.dg1cloud.repository.RegDocumentRepository;
1928
2029 @Service("utils")
2130 public class Utils {
2231
2332 @Autowired private AppConfig appConfig;
33
+ @Autowired private RegDocumentRepository regDocumentRepository;
34
+ @Autowired private ServletContext servletContext;
2435
2536 private static final Logger logger = LoggerFactory.getLogger(CloudService.class);
2637
2738 public URL generateUrl(RegDocument regDocument) throws MalformedURLException {
2839 logger.debug("Genero l'url per il record file {} con id {}", regDocument.getFileName(), regDocument.getDocumentId());
2940 UriBuilder uriBuilder = UriComponentsBuilder.fromUriString(appConfig.getExternalBaseUrl());
30
- uriBuilder.path("/downloadFile");
41
+ uriBuilder.path("/downloadPage");
3142 uriBuilder.queryParam("fileName", regDocument.getFileName());
3243 uriBuilder.queryParam("id", regDocument.getDocumentId());
3344 if ( Strings.isEmpty(regDocument.getSecretKey()) == false ) {
....@@ -53,4 +64,51 @@
5364 logger.debug("La directory {} non e' vuota", directory.getAbsolutePath());
5465 }
5566 }
67
+
68
+ public ResponseEntity<InputStreamResource> getDownloadResponseEntity(String fileName, long id, String secretKey)
69
+ throws FileNotFoundException {
70
+ logger.debug("Avvio download file {} con id {}", fileName, id);
71
+
72
+ MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
73
+ try {
74
+ mediaType = MediaType.parseMediaType(servletContext.getMimeType(fileName));
75
+ } catch (Exception e) {
76
+ logger.warn("Errore nello stabilire il mime type del file. VerrĂ  usato " + mediaType.toString(), e);
77
+ }
78
+
79
+ RegDocument regDocument = regDocumentRepository.getOne(id);
80
+
81
+ if (regDocument.getFileName().equalsIgnoreCase(fileName) == false ) {
82
+ logger.error("Il nome del file richiesto non corrisponde con quello referenziato dall'id");
83
+ throw new RuntimeException("Il nome del file richiesto non corrisponde con quello referenziato dall'id");
84
+ }
85
+
86
+ if ( regDocument.getSecretKey() != null ) {
87
+ logger.debug("E' stata specificata una secretKey. Avvio le verifiche.");
88
+ if ( Strings.isEmpty(secretKey) == true ) {
89
+ logger.error("Non e' stata inviata la secretKey");
90
+ throw new RuntimeException("Per scaricare il file occorre specificare la secretKey");
91
+ } else {
92
+ logger.debug("Controllo corrispondenza della secretKey");
93
+ if (secretKey.equals(regDocument.getSecretKey()) == false ) {
94
+ logger.error("La secretKey inviata non corrisponde a quella impostata in fase di richiesta salvataggio del file");
95
+ throw new RuntimeException("La secretKey inviata non corrisponde a quella impostata in fase di richiesta salvataggio del file");
96
+ } else {
97
+ logger.debug("SecretKey verificata correttamente");
98
+ }
99
+ }
100
+ }
101
+
102
+ File file = new File(regDocument.getFilePath());
103
+ FileInputStream fis = new FileInputStream(file);
104
+ InputStreamResource isr = new InputStreamResource(fis);
105
+ return ResponseEntity.ok()
106
+ // Content-Disposition
107
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
108
+ // Content-Type
109
+ .contentType(mediaType)
110
+ // Contet-Length
111
+ .contentLength(file.length()) //
112
+ .body(isr);
113
+ }
56114 }
dg1cloud-core/src/main/resources/templates/downloadFileCaptcha.html
....@@ -0,0 +1,48 @@
1
+<!DOCTYPE html>
2
+<html xmlns:th="http://www.thymeleaf.org">
3
+ <head>
4
+ <meta charset="utf-8"/>
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
7
+
8
+ <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.min.css}"/>
9
+ <link rel="stylesheet" type="text/css" th:href="@{/css/main.css}"/>
10
+
11
+ <title>Link download file</title>
12
+ <script src='https://www.google.com/recaptcha/api.js'></script>
13
+ </head>
14
+ <body>
15
+ <div class="container">
16
+ <div class="row">
17
+ <div class="col-md-4 col-md-offset-4">
18
+ <div class="panel panel-default">
19
+ <div class="panel-body">
20
+ <div class="text-center">
21
+ <h3><i class="glyphicon glyphicon-lock" style="font-size:2em;"></i></h3>
22
+ <h2 class="text-center" th:text="*{downloadFileCaptchaForm.fileName}"></h2>
23
+ <div class="panel-body">
24
+ <form action="#" th:action="@{/downloadFileCaptcha}" th:object="${downloadFileCaptchaForm}" method="post">
25
+ <input type="hidden" th:field="*{id}" />
26
+ <input type="hidden" th:field="*{fileName}" />
27
+ <input type="hidden" th:field="*{secretKey}" />
28
+ <div class="form-group">
29
+ <div class="g-recaptcha" th:attr="data-sitekey=${@captchaSettings.getKey()}"></div>
30
+ <p class="error-message"
31
+ th:each="error: ${#fields.errors('reCaptchaResponse')}"
32
+ th:text="${error}">Errore validazione reCaptcha</p>
33
+ </div>
34
+ <div class="form-group">
35
+ <button name="downloadFile" type="submit" th:text="'Scarica il file ' + *{fileName} "></button>
36
+ </div>
37
+ </form>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ <script type="text/javascript" th:src="@{/webjars/jquery/3.2.1/jquery.min.js/}"></script>
46
+ <script type="text/javascript" th:src="@{/webjars/bootstrap/3.3.7/js/bootstrap.min.js}"></script>
47
+ </body>
48
+</html>