diff --git a/stubbornjava-common/src/main/java/com/stubbornjava/common/TemplateHelpers.java b/stubbornjava-common/src/main/java/com/stubbornjava/common/TemplateHelpers.java index 2f1aa853..d883653b 100644 --- a/stubbornjava-common/src/main/java/com/stubbornjava/common/TemplateHelpers.java +++ b/stubbornjava-common/src/main/java/com/stubbornjava/common/TemplateHelpers.java @@ -4,6 +4,8 @@ import java.time.format.DateTimeFormatter; import com.github.jknack.handlebars.Options; +import com.google.common.base.Strings; +import com.typesafe.config.Config; public class TemplateHelpers { static final DateTimeFormatter MMMddyyyyFmt = DateTimeFormatter.ofPattern("MMM dd, yyyy"); @@ -12,4 +14,16 @@ public static CharSequence dateFormat(String dateString, Options options) { LocalDateTime date = LocalDateTime.parse(dateString); return MMMddyyyyFmt.format(date); } + + private static final String cdnHost = Configs.getOrDefault(Configs.properties(), + "cdn.host", + Config::getString, + () -> null); + // This expects the url to be relative (eg. /static/img.jpg) + public static CharSequence cdn(String url) { + if (Strings.isNullOrEmpty(cdnHost)) { + return url; + } + return cdnHost + url; + } } diff --git a/stubbornjava-common/src/main/java/com/stubbornjava/common/Templating.java b/stubbornjava-common/src/main/java/com/stubbornjava/common/Templating.java index 399ad727..adade947 100644 --- a/stubbornjava-common/src/main/java/com/stubbornjava/common/Templating.java +++ b/stubbornjava-common/src/main/java/com/stubbornjava/common/Templating.java @@ -31,7 +31,7 @@ public class Templating { static { Templating.Builder builder = new Templating.Builder() - .withHelper("dateFormat", TemplateHelpers::dateFormat) + .withHelpers(new TemplateHelpers()) .withHelper("md", new MarkdownHelper()) .withHelper(AssignHelper.NAME, AssignHelper.INSTANCE) .register(HumanizeHelper::register); @@ -148,6 +148,12 @@ public Builder withHelper(String helperName, Helper helper) { return this; } + public Builder withHelpers(Object helpers) { + log.debug("using template helpers {}" , helpers.getClass()); + handlebars.registerHelpers(helpers); + return this; + } + public Builder register(Consumer consumer) { log.debug("registering helpers"); consumer.accept(handlebars); diff --git a/stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/Headers.java b/stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/Headers.java index 99b5d732..d8f292d9 100644 --- a/stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/Headers.java +++ b/stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/Headers.java @@ -17,4 +17,12 @@ default Optional getHeader(HttpServerExchange exchange, String header) { RequestHeaderAttribute reqHeader = new RequestHeaderAttribute(new HttpString(header)); return Optional.ofNullable(reqHeader.readAttribute(exchange)); } + + default void setHeader(HttpServerExchange exchange, HttpString header, String value) { + exchange.getResponseHeaders().add(header, value); + } + + default void setHeader(HttpServerExchange exchange, String header, String value) { + exchange.getResponseHeaders().add(new HttpString(header), value); + } } diff --git a/stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/handlers/CustomHandlers.java b/stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/handlers/CustomHandlers.java index d96960a9..a9fd494f 100644 --- a/stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/handlers/CustomHandlers.java +++ b/stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/handlers/CustomHandlers.java @@ -3,6 +3,7 @@ import java.io.File; import java.nio.file.Paths; +import java.util.Set; import java.util.SortedMap; import org.slf4j.Logger; @@ -168,4 +169,18 @@ public static HttpHandler securityHeaders(HttpHandler next, ReferrerPolicy polic return security.complete(next); } // {{end:securityHeaders}} + + public static HttpHandler corsOriginWhitelist(HttpHandler next, Set originWhitelist) { + return exchange -> { + String origin = Exchange.headers() + .getHeader(exchange, Headers.ORIGIN) + .orElse(""); + log.debug("Origin: {} Whitelist: {}", origin, originWhitelist); + if (originWhitelist.contains(origin)) { + log.debug("Origin whitelist matched adding CORS header"); + Exchange.headers().setHeader(exchange, "Access-Control-Allow-Origin", origin); + } + next.handleRequest(exchange); + }; + } } diff --git a/stubbornjava-webapp/src/main/java/com/stubbornjava/webapp/StubbornJavaWebApp.java b/stubbornjava-webapp/src/main/java/com/stubbornjava/webapp/StubbornJavaWebApp.java index d4c94b95..3ecabebc 100644 --- a/stubbornjava-webapp/src/main/java/com/stubbornjava/webapp/StubbornJavaWebApp.java +++ b/stubbornjava-webapp/src/main/java/com/stubbornjava/webapp/StubbornJavaWebApp.java @@ -7,6 +7,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.Sets; import com.stubbornjava.common.seo.SitemapRoutes; import com.stubbornjava.common.undertow.SimpleServer; import com.stubbornjava.common.undertow.handlers.CustomHandlers; @@ -37,27 +38,28 @@ private static HttpHandler exceptionHandler(HttpHandler next) { // {{start:csp}} private static HttpHandler contentSecurityPolicy(HttpHandler delegate) { return new ContentSecurityPolicyHandler.Builder() - .defaultSrc(ContentSecurityPolicy.SELF) - .scriptSrc(ContentSecurityPolicy.SELF.getValue(), "https://www.google-analytics.com") + .defaultSrc(ContentSecurityPolicy.SELF.getValue(), "https://*.stubbornjava.com") + .scriptSrc(ContentSecurityPolicy.SELF.getValue(), "https://*.stubbornjava.com", "https://www.google-analytics.com", "data:") // Drop the wildcard when we host our own images. - .imgSrc(ContentSecurityPolicy.SELF.getValue(), "https://www.google-analytics.com", "*") - .connectSrc(ContentSecurityPolicy.SELF.getValue(), "https://www.google-analytics.com") - .fontSrc(ContentSecurityPolicy.SELF.getValue(), "data:") - .styleSrc(ContentSecurityPolicy.SELF.getValue(), ContentSecurityPolicy.UNSAFE_INLINE.getValue()) + .imgSrc(ContentSecurityPolicy.SELF.getValue(), "https://*.stubbornjava.com", "https://www.google-analytics.com", "data:") + .connectSrc(ContentSecurityPolicy.SELF.getValue(), "https://*.stubbornjava.com", "https://www.google-analytics.com") + .fontSrc(ContentSecurityPolicy.SELF.getValue(), "https://*.stubbornjava.com", "data:") + .styleSrc(ContentSecurityPolicy.SELF.getValue(), ContentSecurityPolicy.UNSAFE_INLINE.getValue(), "https://*.stubbornjava.com") .build(delegate); } // {{end:csp}} // {{start:middleware}} private static HttpHandler wrapWithMiddleware(HttpHandler next) { - return MiddlewareBuilder.begin(PageRoutes::redirector) + return MiddlewareBuilder.begin(ex -> CustomHandlers.accessLog(ex, logger)) + .next(StubbornJavaWebApp::exceptionHandler) + .next(CustomHandlers::statusCodeMetrics) .next(handler -> CustomHandlers.securityHeaders(handler, ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)) .next(StubbornJavaWebApp::contentSecurityPolicy) .next(CustomHandlers::gzip) + .next(h -> CustomHandlers.corsOriginWhitelist(next, Sets.newHashSet("https://www.stubbornjava.com"))) + .next(PageRoutes::redirector) .next(BlockingHandler::new) - .next(ex -> CustomHandlers.accessLog(ex, logger)) - .next(CustomHandlers::statusCodeMetrics) - .next(StubbornJavaWebApp::exceptionHandler) .complete(next); } // {{end:middleware}} diff --git a/stubbornjava-webapp/src/main/resources/sjweb.production.conf b/stubbornjava-webapp/src/main/resources/sjweb.production.conf new file mode 100644 index 00000000..96e73058 --- /dev/null +++ b/stubbornjava-webapp/src/main/resources/sjweb.production.conf @@ -0,0 +1,3 @@ +cdn { + host="https://cdn.stubbornjava.com" +} diff --git a/stubbornjava-webapp/ui/src/common/head.hbs b/stubbornjava-webapp/ui/src/common/head.hbs index 1a4308c0..11d44924 100644 --- a/stubbornjava-webapp/ui/src/common/head.hbs +++ b/stubbornjava-webapp/ui/src/common/head.hbs @@ -3,4 +3,4 @@ {{!-- --}} - + diff --git a/stubbornjava-webapp/ui/src/common/scripts.hbs b/stubbornjava-webapp/ui/src/common/scripts.hbs index 1bfa344b..92a3ba42 100644 --- a/stubbornjava-webapp/ui/src/common/scripts.hbs +++ b/stubbornjava-webapp/ui/src/common/scripts.hbs @@ -1,3 +1,3 @@ {{!-- --}} {{!-- Making this script async breaks the jump to id functionality :( --}} - + diff --git a/stubbornjava-webapp/ui/src/widgets/nav/header.hbs b/stubbornjava-webapp/ui/src/widgets/nav/header.hbs index 42d2d6db..27f7ed5d 100644 --- a/stubbornjava-webapp/ui/src/widgets/nav/header.hbs +++ b/stubbornjava-webapp/ui/src/widgets/nav/header.hbs @@ -1,6 +1,6 @@