Skip to content

Commit

Permalink
Merge 05b56f7 into 909b3e6
Browse files Browse the repository at this point in the history
  • Loading branch information
syjer committed Sep 5, 2019
2 parents 909b3e6 + 05b56f7 commit c7eeb07
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 158 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ buildscript {

//this is for processing the index.html at compile time
classpath "com.github.alfio-event:alf.io-public-frontend:$alfioPublicFrontendVersion"
classpath "ch.digitalfondue.jfiveparse:jfiveparse:0.5.3"
classpath "ch.digitalfondue.jfiveparse:jfiveparse:0.5.4"
//
}

Expand Down Expand Up @@ -139,7 +139,7 @@ dependencies {
/**/
compile "com.openhtmltopdf:openhtmltopdf-core:1.0.0"
compile "com.openhtmltopdf:openhtmltopdf-pdfbox:1.0.0"
compile "ch.digitalfondue.jfiveparse:jfiveparse:0.5.3"
compile "ch.digitalfondue.jfiveparse:jfiveparse:0.5.4"
/**/
compile "com.google.zxing:core:3.4.0"
compile "com.google.zxing:javase:3.4.0"
Expand Down
61 changes: 1 addition & 60 deletions src/main/java/alfio/config/MvcConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,10 @@
*/
package alfio.config;

import alfio.manager.system.ConfigurationManager;
import alfio.model.system.ConfigurationKeys;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
Expand All @@ -36,37 +32,24 @@
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.view.AbstractUrlBasedView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Configuration
@ComponentScan(basePackages = {"alfio.controller", "alfio.config"})
@EnableWebMvc
@EnableJdbcHttpSession(maxInactiveIntervalInSeconds = 4 * 60 * 60) //4h
public class MvcConfiguration implements WebMvcConfigurer {

private final ConfigurationManager configurationManager;
private final Environment environment;
private static final Cache<ConfigurationKeys, String> configurationCache = Caffeine.newBuilder()
.expireAfterWrite(15, TimeUnit.MINUTES)
.build();

@Autowired
public MvcConfiguration(
ConfigurationManager configurationManager,
Environment environment) {
this.configurationManager = configurationManager;
public MvcConfiguration(Environment environment) {
this.environment = environment;
}

Expand All @@ -82,48 +65,6 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) {
.addResourceLocations("/webjars/");
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getCSPInterceptor());
}

private HandlerInterceptor getCSPInterceptor() {
return new HandlerInterceptorAdapter() {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {

//
String reportUri = "";
boolean enabledReport = Boolean.parseBoolean(configurationCache.get(ConfigurationKeys.SECURITY_CSP_REPORT_ENABLED,
k -> configurationManager.getForSystem(k).getValueOrDefault("false")
));
if (enabledReport) {
reportUri = " report-uri " + configurationCache.get(ConfigurationKeys.SECURITY_CSP_REPORT_URI,
k -> configurationManager.getForSystem(k).getValueOrDefault("/report-csp-violation")
);
}
//


// http://www.html5rocks.com/en/tutorials/security/content-security-policy/
// lockdown policy

response.addHeader("Content-Security-Policy", "default-src 'none'; "//block all by default
+ " script-src 'self' https://js.stripe.com https://checkout.stripe.com/ https://m.stripe.network https://api.stripe.com/ https://ssl.google-analytics.com/ https://www.google.com/recaptcha/api.js https://www.gstatic.com/recaptcha/api2/ https://maps.googleapis.com/;"//
+ " style-src 'self' 'unsafe-inline';" // unsafe-inline for style is acceptable...
+ " img-src 'self' https: data:;"//
+ " child-src 'self';"
+ " worker-src 'self';"//webworker
+ " frame-src 'self' https://js.stripe.com https://checkout.stripe.com https://m.stripe.network https://m.stripe.com https://www.google.com;"
+ " font-src 'self';"//
+ " media-src blob: 'self';"//for loading camera api
+ " connect-src 'self' https://checkout.stripe.com https://m.stripe.network https://m.stripe.com https://maps.googleapis.com/ https://geocoder.cit.api.here.com;" //<- currently stripe.js use jsonp but if they switch to xmlhttprequest+cors we will be ready
+ reportUri);
}
};
}

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jacksonMessageConverter());
Expand Down
73 changes: 64 additions & 9 deletions src/main/java/alfio/controller/IndexController.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import alfio.util.TemplateManager;
import ch.digitalfondue.jfiveparse.*;
import lombok.AllArgsConstructor;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
Expand All @@ -54,9 +55,9 @@
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Map;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;

import static alfio.model.system.ConfigurationKeys.ENABLE_CAPTCHA_FOR_LOGIN;
Expand All @@ -70,6 +71,23 @@ public class IndexController {
private static final String TEXT_HTML_CHARSET_UTF_8 = "text/html;charset=UTF-8";
private static final String UTF_8 = "UTF-8";


private static final SecureRandom SECURE_RANDOM = new SecureRandom();

private static final Document INDEX_PAGE;
private static final Document OPEN_GRAPH_PAGE;

static {
try (var idxIs = new ClassPathResource("alfio-public-frontend-index.html").getInputStream();
var idxOpenGraph = new ClassPathResource("alfio/web-templates/event-open-graph-page.html").getInputStream()) {
var parser = new Parser();
INDEX_PAGE = parser.parse(new InputStreamReader(idxIs, StandardCharsets.UTF_8));
OPEN_GRAPH_PAGE = parser.parse(new InputStreamReader(idxOpenGraph, StandardCharsets.UTF_8));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}

private final ConfigurationManager configurationManager;
private final EventRepository eventRepository;
private final Environment environment;
Expand Down Expand Up @@ -131,22 +149,25 @@ public void replyToIndex(@PathVariable(value = "eventShortName", required = fals

response.setContentType(TEXT_HTML_CHARSET_UTF_8);
response.setCharacterEncoding(UTF_8);
var nonce = addCspHeader(response);

if (eventShortName != null && RequestUtils.isSocialMediaShareUA(userAgent) && eventRepository.existsByShortName(eventShortName)) {
try (var is = new ClassPathResource("alfio/web-templates/event-open-graph-page.html").getInputStream(); var os = response.getOutputStream()) {
var res = getOpenGraphPage(is, eventShortName, request, lang);
try (var os = response.getOutputStream()) {
var res = getOpenGraphPage((Document) OPEN_GRAPH_PAGE.cloneNode(true), eventShortName, request, lang);
os.write(res);
}
} else {
try (var is = new ClassPathResource("alfio-public-frontend-index.html").getInputStream(); var os = response.getOutputStream()) {
is.transferTo(os);
try (var os = response.getOutputStream()) {
var idx = INDEX_PAGE.cloneNode(true);
idx.getElementsByTagName("script").forEach(element -> element.setAttribute("nonce", nonce));
os.write(idx.getOuterHTML().getBytes(StandardCharsets.UTF_8));
}
}
}

// see https://github.com/alfio-event/alf.io/issues/708
// use ngrok to test the preview
private byte[] getOpenGraphPage(InputStream is, String eventShortName, ServletWebRequest request, String lang) {
private byte[] getOpenGraphPage(Document eventOpenGraph, String eventShortName, ServletWebRequest request, String lang) {
var event = eventRepository.findByShortName(eventShortName);
var locale = RequestUtils.getMatchingLocale(request, event);
if (lang != null && event.getContentLanguages().stream().map(ContentLanguage::getLanguage).anyMatch(lang::equalsIgnoreCase)) {
Expand All @@ -157,7 +178,6 @@ private byte[] getOpenGraphPage(InputStream is, String eventShortName, ServletWe

var title = messageSourceManager.getMessageSourceForEvent(event).getMessage("event.get-your-ticket-for", new String[] {event.getDisplayName()}, locale);

var eventOpenGraph = new Parser().parse(new InputStreamReader(is, StandardCharsets.UTF_8));
var head = eventOpenGraph.getElementsByTagName("head").get(0);

eventOpenGraph.getElementsByTagName("html").get(0).setAttribute("lang", locale.getLanguage());
Expand Down Expand Up @@ -251,6 +271,8 @@ public void getLoginPage(@RequestParam(value="failed", required = false) String
try (var os = response.getOutputStream()) {
response.setContentType(TEXT_HTML_CHARSET_UTF_8);
response.setCharacterEncoding(UTF_8);
var nonce = addCspHeader(response);
model.addAttribute("nonce", nonce);
templateManager.renderHtml(new ClassPathResource("alfio/web-templates/login.ms"), model.asMap(), os);
}
}
Expand Down Expand Up @@ -281,7 +303,40 @@ public void adminHome(Model model, @Value("${alfio.version}") String version, Ht
try (var os = response.getOutputStream()) {
response.setContentType(TEXT_HTML_CHARSET_UTF_8);
response.setCharacterEncoding(UTF_8);
var nonce = addCspHeader(response);
model.addAttribute("nonce", nonce);
templateManager.renderHtml(new ClassPathResource("alfio/web-templates/admin-index.ms"), model.asMap(), os);
}
}


private static String getNonce() {
var nonce = new byte[16]; //128 bit = 16 bytes
SECURE_RANDOM.nextBytes(nonce);
return Hex.encodeHexString(nonce);
}

public String addCspHeader(HttpServletResponse response) {

String nonce = getNonce();

String reportUri = "";

var conf = configurationManager.getFor(List.of(ConfigurationKeys.SECURITY_CSP_REPORT_ENABLED, ConfigurationKeys.SECURITY_CSP_REPORT_URI), ConfigurationLevel.system());

boolean enabledReport = conf.get(ConfigurationKeys.SECURITY_CSP_REPORT_ENABLED).getValueAsBooleanOrDefault(false);
if (enabledReport) {
reportUri = " report-uri " + conf.get(ConfigurationKeys.SECURITY_CSP_REPORT_URI).getValueOrDefault("/report-csp-violation");
}
//
// https://csp.withgoogle.com/docs/strict-csp.html
// with base-uri set to 'self'

response.addHeader("Content-Security-Policy", "object-src 'none'; "+
"script-src 'nonce-" + nonce + "' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:; " +
"base-uri 'self'; "
+ reportUri);

return nonce;
}
}
Loading

0 comments on commit c7eeb07

Please sign in to comment.