From c56634b2812e9d22a33d3fa5715f923f54477266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Mon, 15 Apr 2024 17:23:37 +0200 Subject: [PATCH] add security context related j2html resources --- gradle.properties | 2 +- .../src/test/java/module-info.java | 2 +- .../client/netty/secure/test/Https2Test.java | 6 +- .../netty/secure/test/JdkClientTest.java | 3 + .../client/netty/secure/test/MockBackOff.java | 4 +- .../http/client/netty/test/ParameterTest.java | 3 + .../j2html/InternalServerErrorHandler.java | 3 +- .../j2html/J2HtmlAuthResourceHandler.java | 34 +++++++ .../xbib/net/http/j2html/J2HtmlResource.java | 57 +++++++++++ .../http/j2html/J2HtmlResourceHandler.java | 46 --------- .../application/web/groovy/Bootstrap.java | 8 +- .../application/web/j2html/Bootstrap.java | 95 ++++++++++++++----- .../secure/test/SimpleHttpsServerTest.java | 3 + .../net/http/server/application/Resolver.java | 1 + .../auth/BasicAuthenticationHandler.java | 2 +- .../auth/FormAuthenticationHandler.java | 75 ++++++--------- .../auth/LoginAuthenticationHandler.java | 31 ++++-- .../server/route/BaseHttpRouterContext.java | 11 +++ .../http/server/route/HttpRouterContext.java | 4 + .../GroovyFormAuthenticationHandler.java | 77 +++++++++++++++ .../groovy/GroovyTemplateServiceBuilder.java | 2 +- 21 files changed, 336 insertions(+), 133 deletions(-) rename {net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web => net-http-j2html/src/main/java/org/xbib/net/http}/j2html/InternalServerErrorHandler.java (93%) create mode 100644 net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlAuthResourceHandler.java create mode 100644 net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResource.java create mode 100644 net-http-template-groovy/src/main/java/org/xbib/net/http/template/groovy/GroovyFormAuthenticationHandler.java diff --git a/gradle.properties b/gradle.properties index c7fa873..2477f3e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group = org.xbib name = net-http -version = 4.4.3 +version = 4.4.4 diff --git a/net-http-client-netty-secure/src/test/java/module-info.java b/net-http-client-netty-secure/src/test/java/module-info.java index d97c5f4..2a57be3 100644 --- a/net-http-client-netty-secure/src/test/java/module-info.java +++ b/net-http-client-netty-secure/src/test/java/module-info.java @@ -13,7 +13,7 @@ module org.xbib.net.http.client.netty.secure.test { requires org.xbib.net.http; requires org.xbib.net.http.client; requires org.xbib.net.http.client.netty; - requires org.xbib.net.http.client.netty.secure; + requires transitive org.xbib.net.http.client.netty.secure; requires io.netty.handler.proxy; exports org.xbib.net.http.client.netty.secure.test; opens org.xbib.net.http.client.netty.secure.test; diff --git a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/Https2Test.java b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/Https2Test.java index 68cb491..6db72c7 100644 --- a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/Https2Test.java +++ b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/Https2Test.java @@ -17,10 +17,12 @@ public class Https2Test { private static final Logger logger = Logger.getLogger(Https2Test.class.getName()); - @Disabled + public Https2Test() { + } + + @Disabled("the xbib server does not offer HTTP/2 so this does not work!") @Test void testXbib() throws Exception { - // the xbib server does not offer HTTP/2 so this does not work! NettyHttpClientConfig config = new NettyHttpsClientConfig() .setDebug(true); try (NettyHttpClient client = NettyHttpClient.builder() diff --git a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/JdkClientTest.java b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/JdkClientTest.java index f92a93d..a9c4c2e 100644 --- a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/JdkClientTest.java +++ b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/JdkClientTest.java @@ -22,6 +22,9 @@ public class JdkClientTest { System.setProperty("javax.net.debug", "true"); } + public JdkClientTest() { + } + @Test public void testDNB() throws Exception { HttpClient httpClient = HttpClient.newBuilder() diff --git a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/MockBackOff.java b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/MockBackOff.java index ae6c9ae..7b1e916 100644 --- a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/MockBackOff.java +++ b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/client/netty/secure/test/MockBackOff.java @@ -1,6 +1,5 @@ package org.xbib.net.http.client.netty.secure.test; - import org.xbib.net.http.client.BackOff; /** @@ -22,6 +21,9 @@ public class MockBackOff implements BackOff { /** Number of tries so far. */ private int numTries; + public MockBackOff() { + } + @Override public void reset() { numTries = 0; diff --git a/net-http-client-netty/src/test/java/org/xbib/net/http/client/netty/test/ParameterTest.java b/net-http-client-netty/src/test/java/org/xbib/net/http/client/netty/test/ParameterTest.java index 70f8d76..9e83d55 100644 --- a/net-http-client-netty/src/test/java/org/xbib/net/http/client/netty/test/ParameterTest.java +++ b/net-http-client-netty/src/test/java/org/xbib/net/http/client/netty/test/ParameterTest.java @@ -10,6 +10,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ParameterTest { + public ParameterTest() { + } + @Test public void testGetParametersWithSharedUrl() { URL url = URL.from("http://example.com"); diff --git a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/InternalServerErrorHandler.java b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/InternalServerErrorHandler.java similarity index 93% rename from net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/InternalServerErrorHandler.java rename to net-http-j2html/src/main/java/org/xbib/net/http/j2html/InternalServerErrorHandler.java index bd2a0cd..cc62aa6 100644 --- a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/InternalServerErrorHandler.java +++ b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/InternalServerErrorHandler.java @@ -1,8 +1,7 @@ -package org.xbib.net.http.server.application.web.j2html; +package org.xbib.net.http.j2html; import org.xbib.net.Attributes; import org.xbib.net.Resource; -import org.xbib.net.http.j2html.J2HtmlResourceHandler; import org.xbib.net.http.server.resource.HtmlTemplateResourceHandler; import org.xbib.net.http.server.route.HttpRouterContext; import org.xbib.net.util.ExceptionFormatter; diff --git a/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlAuthResourceHandler.java b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlAuthResourceHandler.java new file mode 100644 index 0000000..319e01e --- /dev/null +++ b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlAuthResourceHandler.java @@ -0,0 +1,34 @@ +package org.xbib.net.http.j2html; + +import org.xbib.net.Resource; +import org.xbib.net.http.server.resource.HtmlTemplateResourceHandler; +import org.xbib.net.http.server.route.HttpRouterContext; + +import java.io.IOException; +import java.nio.file.Path; + +public class J2HtmlAuthResourceHandler extends HtmlTemplateResourceHandler { + + public J2HtmlAuthResourceHandler() { + this(null, "java", "index.java"); + } + + public J2HtmlAuthResourceHandler(Path root, String suffix, String indexFileName) { + super(root, suffix, indexFileName); + } + + @Override + protected Resource createResource(HttpRouterContext httpRouterContext) throws IOException { + return httpRouterContext.isAuthenticated() ? + createAuthenticatedResource(httpRouterContext) : + createUnauthenticatedResource(httpRouterContext); + } + + protected Resource createAuthenticatedResource(HttpRouterContext httpRouterContext) throws IOException { + return new J2HtmlResource(this, httpRouterContext); + } + + protected Resource createUnauthenticatedResource(HttpRouterContext httpRouterContext) throws IOException { + return new J2HtmlResource(this, httpRouterContext); + } +} diff --git a/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResource.java b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResource.java new file mode 100644 index 0000000..c2dd426 --- /dev/null +++ b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResource.java @@ -0,0 +1,57 @@ +package org.xbib.net.http.j2html; + +import org.xbib.net.buffer.DataBuffer; +import org.xbib.net.http.HttpResponseStatus; +import org.xbib.net.http.server.HttpRequest; +import org.xbib.net.http.server.resource.HtmlTemplateResource; +import org.xbib.net.http.server.resource.HtmlTemplateResourceHandler; +import org.xbib.net.http.server.route.HttpRouterContext; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static org.xbib.j2html.TagCreator.body; +import static org.xbib.j2html.TagCreator.h1; +import static org.xbib.net.http.HttpHeaderNames.CONTENT_TYPE; + +public class J2HtmlResource extends HtmlTemplateResource { + + protected HttpRequest request; + + public J2HtmlResource(HtmlTemplateResourceHandler templateResourceHandler, + HttpRouterContext httpRouterContext) throws IOException { + super(templateResourceHandler, httpRouterContext); + } + + @Override + public void render(HttpRouterContext context) throws IOException { + this.request = context.getRequest(); + Objects.requireNonNull(responseBuilder); + DataBuffer dataBuffer = context.getDataBufferFactory().allocateBuffer(); + dataBuffer.write(renderBody(context), StandardCharsets.UTF_8); + HttpResponseStatus httpResponseStatus = responseBuilder.getResponseStatus(); + if (httpResponseStatus == null) { + httpResponseStatus = HttpResponseStatus.OK; + } + context.status(httpResponseStatus) + .header("cache-control", "no-cache") // override default must-revalidate behavior + .header("content-length", Integer.toString(dataBuffer.writePosition())) + .header(CONTENT_TYPE, "text/html; charset=" + StandardCharsets.UTF_8.displayName()) + .body(dataBuffer); + } + + /** + * Rendering a body. + * By subclassing this handler, this method should be overridden by custom j2html code. + * The body here is just a greeting as an example. + * + * @param context the router context + * @return the body string fo the HTTP response + */ + protected String renderBody(HttpRouterContext context) { + return body( + h1("Hello World") + ).render(); + } +} diff --git a/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResourceHandler.java b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResourceHandler.java index 3336382..3ffd985 100644 --- a/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResourceHandler.java +++ b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResourceHandler.java @@ -1,21 +1,14 @@ package org.xbib.net.http.j2html; import org.xbib.net.Resource; -import org.xbib.net.buffer.DataBuffer; -import org.xbib.net.http.HttpResponseStatus; -import org.xbib.net.http.server.HttpRequest; -import org.xbib.net.http.server.resource.HtmlTemplateResource; import org.xbib.net.http.server.resource.HtmlTemplateResourceHandler; import org.xbib.net.http.server.route.HttpRouterContext; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; -import java.util.Objects; import static org.xbib.j2html.TagCreator.body; import static org.xbib.j2html.TagCreator.h1; -import static org.xbib.net.http.HttpHeaderNames.CONTENT_TYPE; public class J2HtmlResourceHandler extends HtmlTemplateResourceHandler { @@ -31,43 +24,4 @@ public class J2HtmlResourceHandler extends HtmlTemplateResourceHandler { protected Resource createResource(HttpRouterContext httpRouterContext) throws IOException { return new J2HtmlResource(this, httpRouterContext); } - - protected static class J2HtmlResource extends HtmlTemplateResource { - - protected HttpRequest request; - - protected J2HtmlResource(HtmlTemplateResourceHandler templateResourceHandler, - HttpRouterContext httpRouterContext) throws IOException { - super(templateResourceHandler, httpRouterContext); - } - - @Override - public void render(HttpRouterContext context) throws IOException { - this.request = context.getRequest(); - Objects.requireNonNull(responseBuilder); - DataBuffer dataBuffer = context.getDataBufferFactory().allocateBuffer(); - dataBuffer.write(renderBody(context), StandardCharsets.UTF_8); - HttpResponseStatus httpResponseStatus = responseBuilder.getResponseStatus(); - if (httpResponseStatus == null) { - httpResponseStatus = HttpResponseStatus.OK; - } - context.status(httpResponseStatus) - .header("cache-control", "no-cache") // override default must-revalidate behavior - .header("content-length", Integer.toString(dataBuffer.writePosition())) - .header(CONTENT_TYPE, "text/html; charset=" + StandardCharsets.UTF_8.displayName()) - .body(dataBuffer); - } - - /** - * By subclassing this handler, this method should be overriden by custom j2html code. - * The body here is just a greeting as an example. - * @param context the router context - * @return the body string fo the HTTP response - */ - protected String renderBody(HttpRouterContext context) { - return body( - h1("Hello World") - ).render(); - } - } } diff --git a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/groovy/Bootstrap.java b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/groovy/Bootstrap.java index fa22da5..de6628d 100644 --- a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/groovy/Bootstrap.java +++ b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/groovy/Bootstrap.java @@ -24,7 +24,6 @@ import org.xbib.net.http.server.executor.Executor; import org.xbib.net.http.server.route.HttpRouter; import org.xbib.net.http.server.service.HttpService; import org.xbib.net.http.server.auth.BasicAuthenticationHandler; -import org.xbib.net.http.server.auth.FormAuthenticationHandler; import org.xbib.net.http.server.ldap.LdapContextFactory; import org.xbib.net.http.server.ldap.LdapGroupMapping; import org.xbib.net.http.server.ldap.LdapRealm; @@ -37,6 +36,7 @@ import org.xbib.net.http.server.netty.secure.HttpsAddress; import org.xbib.net.http.server.netty.secure.HttpsRequest; import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig; import org.xbib.net.http.server.resource.ClassLoaderResourceHandler; +import org.xbib.net.http.template.groovy.GroovyFormAuthenticationHandler; import org.xbib.net.http.template.groovy.GroovyInternalServerErrorHandler; import org.xbib.net.http.template.groovy.GroovyHttpStatusHandler; import org.xbib.net.http.template.groovy.GroovyTemplateResourceHandler; @@ -125,9 +125,9 @@ public final class Bootstrap { BasicAuthenticationHandler basicAuthenticationHandler = new BasicAuthenticationHandler(ldapRealm); - FormAuthenticationHandler formAuthenticationHandler = - new FormAuthenticationHandler("j_username", "j_password", "j_remember", - "demo/auth/form/index.gtpl", ldapRealm); + GroovyFormAuthenticationHandler formAuthenticationHandler = + new GroovyFormAuthenticationHandler("j_username", "j_password", + ldapRealm, "demo/auth/form/index.gtpl"); HttpSecurityDomain securityDomain = BaseHttpSecurityDomain.builder() .setSecurityRealm(ldapRealm) diff --git a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/Bootstrap.java b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/Bootstrap.java index d3faca1..262707d 100644 --- a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/Bootstrap.java +++ b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/Bootstrap.java @@ -5,11 +5,14 @@ import org.xbib.config.ConfigLogger; import org.xbib.config.ConfigParams; import org.xbib.config.SystemConfigLogger; import org.xbib.net.NetworkClass; +import org.xbib.net.Resource; import org.xbib.net.http.HttpHeaderNames; import org.xbib.net.http.HttpHeaderValues; import org.xbib.net.http.HttpResponseStatus; import org.xbib.net.http.HttpVersion; -import org.xbib.net.http.j2html.J2HtmlResourceHandler; +import org.xbib.net.http.j2html.InternalServerErrorHandler; +import org.xbib.net.http.j2html.J2HtmlAuthResourceHandler; +import org.xbib.net.http.j2html.J2HtmlResource; import org.xbib.net.http.j2html.J2HtmlService; import org.xbib.net.http.server.application.web.WebApplication; import org.xbib.net.http.server.auth.BasicAuthenticationHandler; @@ -29,12 +32,14 @@ import org.xbib.net.http.server.netty.secure.HttpsAddress; import org.xbib.net.http.server.netty.secure.HttpsRequest; import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig; import org.xbib.net.http.server.resource.ClassLoaderResourceHandler; +import org.xbib.net.http.server.resource.HtmlTemplateResourceHandler; import org.xbib.net.http.server.route.BaseHttpRouter; import org.xbib.net.http.server.route.HttpRouter; +import org.xbib.net.http.server.route.HttpRouterContext; import org.xbib.net.http.server.service.BaseHttpService; -import org.xbib.net.http.server.service.HttpService; import org.xbib.settings.Settings; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -42,6 +47,9 @@ import java.util.Objects; import java.util.Optional; import java.util.ServiceLoader; +import static org.xbib.j2html.TagCreator.body; +import static org.xbib.j2html.TagCreator.h1; + public final class Bootstrap { private static final ConfigLogger bootLogger; @@ -125,32 +133,13 @@ public final class Bootstrap { new BasicAuthenticationHandler(ldapRealm); FormAuthenticationHandler formAuthenticationHandler = - new FormAuthenticationHandler("j_username", "j_password", "j_remember", - "demo/auth/form/index.gtpl", ldapRealm); + new FormAuthenticationHandler("_username", "_password", ldapRealm); HttpSecurityDomain securityDomain = BaseHttpSecurityDomain.builder() .setSecurityRealm(ldapRealm) .setHandlers(formAuthenticationHandler) .build(); - HttpService httpService = BaseHttpService.builder() - .setPath("/secure") - .setHandler(ctx -> { - ctx.status(HttpResponseStatus.OK) - .header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN) - .charset(StandardCharsets.UTF_8) - .body("secure domain: " + - " SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() + - " SSL session = " + ctx.getRequest().as(HttpsRequest.class).getSSLSession() + - " base URL = " + ctx.getRequest().getBaseURL() + - " parameter = " + ctx.getRequest().getParameter().toString() + - " attributes = " + ctx.getAttributes() + - " local address = " + ctx.getRequest().getLocalAddress() + - " remote address = " + ctx.getRequest().getRemoteAddress()); - ctx.done(); - }) - .build(); - HttpRouter httpRouter = BaseHttpRouter.builder() .setHandler(500, new InternalServerErrorHandler()) .addDomain(BaseHttpDomain.builder() @@ -168,10 +157,27 @@ public final class Bootstrap { .setPath("/webjars/**") .setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/")) .build()) - .addService(httpService) + .addService(BaseHttpService.builder() + .setPath("/insecure") + .setHandler(ctx -> { + ctx.status(HttpResponseStatus.OK) + .header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN) + .charset(StandardCharsets.UTF_8) + .body("secure domain: " + + " SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() + + " SSL session = " + ctx.getRequest().as(HttpsRequest.class).getSSLSession() + + " base URL = " + ctx.getRequest().getBaseURL() + + " parameter = " + ctx.getRequest().getParameter().toString() + + " attributes = " + ctx.getAttributes() + + " local address = " + ctx.getRequest().getLocalAddress() + + " remote address = " + ctx.getRequest().getRemoteAddress()); + ctx.done(); + }) + .build()) .addService(J2HtmlService.builder() .setPath("glob:**") - .setHandler(new J2HtmlResourceHandler()) + .setSecurityDomain(securityDomain) + .setHandler(new MyResourceHandler()) .build()) .build()) .build(); @@ -197,6 +203,47 @@ public final class Bootstrap { return 0; } + static class MyResourceHandler extends J2HtmlAuthResourceHandler { + + @Override + protected Resource createAuthenticatedResource(HttpRouterContext httpRouterContext) throws IOException { + return new DemoResource(this, httpRouterContext); + } + + @Override + protected Resource createUnauthenticatedResource(HttpRouterContext httpRouterContext) throws IOException { + return new UnauthenticatedDemoResource(this, httpRouterContext); + } + } + + static class DemoResource extends J2HtmlResource { + + public DemoResource(HtmlTemplateResourceHandler templateResourceHandler, + HttpRouterContext httpRouterContext) throws IOException { + super(templateResourceHandler, httpRouterContext); + } + + protected String renderBody(HttpRouterContext context) { + return body( + h1("This is a demo") + ).render(); + } + } + + static class UnauthenticatedDemoResource extends J2HtmlResource { + + public UnauthenticatedDemoResource(HtmlTemplateResourceHandler templateResourceHandler, + HttpRouterContext httpRouterContext) throws IOException { + super(templateResourceHandler, httpRouterContext); + } + + protected String renderBody(HttpRouterContext context) { + return body( + h1("This is a demo, unauthenticated") + ).render(); + } + } + private static final String hexFavIcon = "000001000100101000000100200068040000160000002800000010000000" + "200000000100200000000000000000000000000000000000000000000000" + diff --git a/net-http-server-simple-secure/src/test/java/org/xbib/net/http/server/simple/secure/test/SimpleHttpsServerTest.java b/net-http-server-simple-secure/src/test/java/org/xbib/net/http/server/simple/secure/test/SimpleHttpsServerTest.java index 50f589f..42ab4f8 100644 --- a/net-http-server-simple-secure/src/test/java/org/xbib/net/http/server/simple/secure/test/SimpleHttpsServerTest.java +++ b/net-http-server-simple-secure/src/test/java/org/xbib/net/http/server/simple/secure/test/SimpleHttpsServerTest.java @@ -26,6 +26,9 @@ public class SimpleHttpsServerTest { private static final Logger logger = Logger.getLogger(SimpleHttpsServerTest.class.getName()); + public SimpleHttpsServerTest() { + } + @Test public void simpleSecureHttpsServerTest() throws Exception { HttpsAddress httpsAddress = HttpsAddress.builder() diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/application/Resolver.java b/net-http-server/src/main/java/org/xbib/net/http/server/application/Resolver.java index 3dcf119..f255a46 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/application/Resolver.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/application/Resolver.java @@ -1,5 +1,6 @@ package org.xbib.net.http.server.application; +@FunctionalInterface public interface Resolver { R resolve(String string); diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/auth/BasicAuthenticationHandler.java b/net-http-server/src/main/java/org/xbib/net/http/server/auth/BasicAuthenticationHandler.java index 109b9c5..dfeec2b 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/auth/BasicAuthenticationHandler.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/auth/BasicAuthenticationHandler.java @@ -50,6 +50,6 @@ public class BasicAuthenticationHandler extends LoginAuthenticationHandler imple } logger.log(Level.INFO, "unauthenticated"); context.status(HttpResponseStatus.UNAUTHORIZED) - .header("WWW-Authenticate", "Basic realm=\"" + securityRealm.getName() + "\""); + .header("WWW-Authenticate", "Basic realm=\"" + getSecurityRealm().getName() + "\""); } } diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/auth/FormAuthenticationHandler.java b/net-http-server/src/main/java/org/xbib/net/http/server/auth/FormAuthenticationHandler.java index fd7e8c9..e9177e2 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/auth/FormAuthenticationHandler.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/auth/FormAuthenticationHandler.java @@ -15,73 +15,60 @@ public class FormAuthenticationHandler extends LoginAuthenticationHandler implem private static final Logger logger = Logger.getLogger(FormAuthenticationHandler.class.getName()); - String usernameParameter; - - String passwordParameter; - - String rememberParameter; - - String loginPage; - public FormAuthenticationHandler(String usernameParameter, String passwordParameter, - String rememberParameter, - String loginPage, SecurityRealm securityRealm) { super(usernameParameter, passwordParameter, securityRealm); - this.usernameParameter = usernameParameter; - this.passwordParameter = passwordParameter; - this.rememberParameter = rememberParameter; - this.loginPage = loginPage; } @Override public void handle(HttpRouterContext context) throws IOException { - if (loginPage == null) { - logger.log(Level.WARNING, "no loginPage configured"); - return; - } UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile"); if (userProfile != null && userProfile.getUserId() != null) { logger.log(Level.FINE, "user id already set: " + userProfile.getUserId()); return; } - // always add an "anonymous" user profile + if (context.isAuthenticated()) { + logger.log(Level.FINE, "context already authenticated"); + return; + } + // always add an "anonymous" user profile with a null user ID userProfile = new BaseUserProfile(); context.getAttributes().put("userprofile", userProfile); - Parameter parameter = context.getRequest().getParameter(); - if (!parameter.containsKey(usernameParameter, Parameter.Domain.FORM)) { - logger.log(Level.WARNING, "usernameParameter not set, unable to authenticate"); - prepareFormAuthentication(context); - return; - } - if (!parameter.containsKey(passwordParameter, Parameter.Domain.FORM)) { - logger.log(Level.WARNING, "passwordParameter not set, unable to authenticate"); - prepareFormAuthentication(context); - return; - } + boolean isAuthenticated = false; try { - String username = parameter.getAsString(usernameParameter, Parameter.Domain.FORM); - String password = parameter.getAsString(passwordParameter, Parameter.Domain.FORM); - logger.log(Level.FINE, "username and password found, ready for authentication"); + Parameter parameter = context.getRequest().getParameter(); + if (!parameter.containsKey(getUserParameterName(), Parameter.Domain.FORM)) { + logger.log(Level.WARNING, "usernameParameter not set, unable to authenticate"); + prepareFormAuthentication(context); + return; + } + String username = parameter.getAsString(getUserParameterName(), Parameter.Domain.FORM); + if (!parameter.containsKey(getPasswordParameterName(), Parameter.Domain.FORM)) { + logger.log(Level.WARNING, "passwordParameter not set, unable to authenticate"); + prepareFormAuthentication(context); + return; + } + String password = parameter.getAsString(getPasswordParameterName(), Parameter.Domain.FORM); authenticate(userProfile, username, password, context.getRequest()); - logger.log(Level.FINE, "successful authentication"); - return; + isAuthenticated = true; } catch (ParameterException e) { logger.log(Level.SEVERE, "parameter error"); } catch (Exception e) { logger.log(Level.SEVERE, "authentication error"); + } finally { + context.setAuthenticated(isAuthenticated); + if (!isAuthenticated) { + prepareFormAuthentication(context); + } } - prepareFormAuthentication(context); } - private void prepareFormAuthentication(HttpRouterContext context) { - // this will redirect internally to login page, and back to the original path. - // We need a full path resolve against the server URL. - logger.log(Level.FINE, "templatePath = " + loginPage); - context.getAttributes().put("templatePath", loginPage); - URL loc = context.getContextURL().resolve(context.getRequest().getRequestURI()).normalize(); - logger.log(Level.FINE, "context URL = " + context.getContextURL() + " request URI = " + context.getRequest().getRequestURI() + " loc = " + loc); - context.getAttributes().put("originalPath", loc.toExternalForm()); + protected void prepareFormAuthentication(HttpRouterContext context) { + URL origin = context.getContextURL().resolve(context.getRequest().getRequestURI()).normalize(); + logger.log(Level.FINE, "context URL = " + context.getContextURL() + + " request URI = " + context.getRequest().getRequestURI() + + " origin = " + origin); + context.getAttributes().put("_origin", origin.toExternalForm()); } } diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/auth/LoginAuthenticationHandler.java b/net-http-server/src/main/java/org/xbib/net/http/server/auth/LoginAuthenticationHandler.java index db048ce..e6abb3f 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/auth/LoginAuthenticationHandler.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/auth/LoginAuthenticationHandler.java @@ -7,6 +7,7 @@ import java.util.logging.Logger; import org.xbib.net.Authenticator; import org.xbib.net.GroupsProvider; import org.xbib.net.Parameter; +import org.xbib.net.ParameterException; import org.xbib.net.Request; import org.xbib.net.SecurityRealm; import org.xbib.net.UserDetails; @@ -23,7 +24,7 @@ public class LoginAuthenticationHandler implements HttpHandler { private final String passwordParameterName; - protected final SecurityRealm securityRealm; + private final SecurityRealm securityRealm; public LoginAuthenticationHandler(String userParameterName, String passwordParameterName, @@ -33,21 +34,39 @@ public class LoginAuthenticationHandler implements HttpHandler { this.securityRealm = securityRealm; } + public String getUserParameterName() { + return userParameterName; + } + + public String getPasswordParameterName() { + return passwordParameterName; + } + + public SecurityRealm getSecurityRealm() { + return securityRealm; + } + @Override public void handle(HttpRouterContext context) throws IOException { UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile"); if (userProfile != null && userProfile.getUserId() != null) { return; } + Parameter parameter = context.getRequest().getParameter(); userProfile = new BaseUserProfile(); + context.getAttributes().put("userprofile", userProfile); + boolean isAuthenticated = false; try { - authenticate(userProfile, - (String) context.getRequest().getParameter().get(userParameterName, Parameter.Domain.FORM), - (String) context.getRequest().getParameter().get(passwordParameterName, Parameter.Domain.FORM), - context.getRequest()); - context.getAttributes().put("userprofile", userProfile); + String username = parameter.getAsString(getUserParameterName(), Parameter.Domain.FORM); + String password = parameter.getAsString(getPasswordParameterName(), Parameter.Domain.FORM); + authenticate(userProfile, username, password, context.getRequest()); + isAuthenticated = true; + } catch (ParameterException e) { + logger.log(Level.SEVERE, "parameter error"); } catch (Exception e) { logger.log(Level.SEVERE, "authentication error"); + } finally { + context.setAuthenticated(isAuthenticated); } } diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouterContext.java b/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouterContext.java index 0c81781..6c76c49 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouterContext.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouterContext.java @@ -57,6 +57,8 @@ public class BaseHttpRouterContext implements HttpRouterContext { private boolean next; + private boolean isAuthenticated; + public BaseHttpRouterContext(Application application, HttpDomain domain, HttpRequestBuilder httpRequestBuilder, @@ -280,6 +282,15 @@ public class BaseHttpRouterContext implements HttpRouterContext { return httpResponseBuilder.getLength(); } + public void setAuthenticated(boolean isAuthenticated) { + this.isAuthenticated = isAuthenticated; + } + + @Override + public boolean isAuthenticated() { + return isAuthenticated; + } + @Override public void flush() throws IOException { httpResponseBuilder.build().flush(); diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/route/HttpRouterContext.java b/net-http-server/src/main/java/org/xbib/net/http/server/route/HttpRouterContext.java index ec97cbb..a5b4ff8 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/route/HttpRouterContext.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/route/HttpRouterContext.java @@ -97,6 +97,10 @@ public interface HttpRouterContext { long lengthInBytes(); + void setAuthenticated(boolean isAuthenticated); + + boolean isAuthenticated(); + void flush() throws IOException; void close() throws IOException; diff --git a/net-http-template-groovy/src/main/java/org/xbib/net/http/template/groovy/GroovyFormAuthenticationHandler.java b/net-http-template-groovy/src/main/java/org/xbib/net/http/template/groovy/GroovyFormAuthenticationHandler.java new file mode 100644 index 0000000..470f9e9 --- /dev/null +++ b/net-http-template-groovy/src/main/java/org/xbib/net/http/template/groovy/GroovyFormAuthenticationHandler.java @@ -0,0 +1,77 @@ +package org.xbib.net.http.template.groovy; + +import org.xbib.net.Parameter; +import org.xbib.net.ParameterException; +import org.xbib.net.SecurityRealm; +import org.xbib.net.URL; +import org.xbib.net.UserProfile; +import org.xbib.net.http.server.auth.BaseUserProfile; +import org.xbib.net.http.server.auth.FormAuthenticationHandler; +import org.xbib.net.http.server.route.HttpRouterContext; + +import java.io.IOException; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class GroovyFormAuthenticationHandler extends FormAuthenticationHandler { + + private static final Logger logger = Logger.getLogger(GroovyFormAuthenticationHandler.class.getName()); + + private final String loginTemplate; + + public GroovyFormAuthenticationHandler(String usernameParameter, + String passwordParameter, + SecurityRealm securityRealm, + String loginTemplate) { + super(usernameParameter, passwordParameter, securityRealm); + this.loginTemplate = Objects.requireNonNull(loginTemplate, "no login template configured"); + } + + @Override + public void handle(HttpRouterContext context) throws IOException { + UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile"); + if (userProfile != null && userProfile.getUserId() != null) { + logger.log(Level.FINE, "user id already set: " + userProfile.getUserId()); + return; + } + // always add an "anonymous" user profile + userProfile = new BaseUserProfile(); + context.getAttributes().put("userprofile", userProfile); + Parameter parameter = context.getRequest().getParameter(); + if (!parameter.containsKey(getUserParameterName(), Parameter.Domain.FORM)) { + logger.log(Level.WARNING, "usernameParameter not set, unable to authenticate"); + prepareFormAuthentication(context); + return; + } + if (!parameter.containsKey(getPasswordParameterName(), Parameter.Domain.FORM)) { + logger.log(Level.WARNING, "passwordParameter not set, unable to authenticate"); + prepareFormAuthentication(context); + return; + } + try { + String username = parameter.getAsString(getUserParameterName(), Parameter.Domain.FORM); + String password = parameter.getAsString(getPasswordParameterName(), Parameter.Domain.FORM); + logger.log(Level.FINE, "username and password found, ready for authentication"); + authenticate(userProfile, username, password, context.getRequest()); + logger.log(Level.FINE, "successful authentication"); + return; + } catch (ParameterException e) { + logger.log(Level.SEVERE, "parameter error"); + } catch (Exception e) { + logger.log(Level.SEVERE, "authentication error"); + } + prepareFormAuthentication(context); + } + + @Override + protected void prepareFormAuthentication(HttpRouterContext context) { + // this will redirect internally to login page, and back to the original path. + // We need a full path resolve against the server URL. + logger.log(Level.FINE, "set new templatePath, login template = " + loginTemplate); + context.getAttributes().put("templatePath", loginTemplate); + URL loc = context.getContextURL().resolve(context.getRequest().getRequestURI()).normalize(); + logger.log(Level.FINE, "context URL = " + context.getContextURL() + " request URI = " + context.getRequest().getRequestURI() + " loc = " + loc); + context.getAttributes().put("originalPath", loc.toExternalForm()); + } +} diff --git a/net-http-template-groovy/src/main/java/org/xbib/net/http/template/groovy/GroovyTemplateServiceBuilder.java b/net-http-template-groovy/src/main/java/org/xbib/net/http/template/groovy/GroovyTemplateServiceBuilder.java index 99250ec..4c93e04 100644 --- a/net-http-template-groovy/src/main/java/org/xbib/net/http/template/groovy/GroovyTemplateServiceBuilder.java +++ b/net-http-template-groovy/src/main/java/org/xbib/net/http/template/groovy/GroovyTemplateServiceBuilder.java @@ -50,7 +50,7 @@ public class GroovyTemplateServiceBuilder extends BaseHttpServiceBuilder { } @Override - public BaseHttpServiceBuilder setSecurityDomain(HttpSecurityDomain securityDomain) { + public GroovyTemplateServiceBuilder setSecurityDomain(HttpSecurityDomain securityDomain) { super.setSecurityDomain(securityDomain); return this; }