add security context related j2html resources

This commit is contained in:
Jörg Prante 2024-04-15 17:23:37 +02:00
parent 6e83937cc1
commit c56634b281
21 changed files with 336 additions and 133 deletions

View file

@ -1,3 +1,3 @@
group = org.xbib group = org.xbib
name = net-http name = net-http
version = 4.4.3 version = 4.4.4

View file

@ -13,7 +13,7 @@ module org.xbib.net.http.client.netty.secure.test {
requires org.xbib.net.http; requires org.xbib.net.http;
requires org.xbib.net.http.client; requires org.xbib.net.http.client;
requires org.xbib.net.http.client.netty; 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; requires io.netty.handler.proxy;
exports org.xbib.net.http.client.netty.secure.test; exports org.xbib.net.http.client.netty.secure.test;
opens org.xbib.net.http.client.netty.secure.test; opens org.xbib.net.http.client.netty.secure.test;

View file

@ -17,10 +17,12 @@ public class Https2Test {
private static final Logger logger = Logger.getLogger(Https2Test.class.getName()); 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 @Test
void testXbib() throws Exception { void testXbib() throws Exception {
// the xbib server does not offer HTTP/2 so this does not work!
NettyHttpClientConfig config = new NettyHttpsClientConfig() NettyHttpClientConfig config = new NettyHttpsClientConfig()
.setDebug(true); .setDebug(true);
try (NettyHttpClient client = NettyHttpClient.builder() try (NettyHttpClient client = NettyHttpClient.builder()

View file

@ -22,6 +22,9 @@ public class JdkClientTest {
System.setProperty("javax.net.debug", "true"); System.setProperty("javax.net.debug", "true");
} }
public JdkClientTest() {
}
@Test @Test
public void testDNB() throws Exception { public void testDNB() throws Exception {
HttpClient httpClient = HttpClient.newBuilder() HttpClient httpClient = HttpClient.newBuilder()

View file

@ -1,6 +1,5 @@
package org.xbib.net.http.client.netty.secure.test; package org.xbib.net.http.client.netty.secure.test;
import org.xbib.net.http.client.BackOff; import org.xbib.net.http.client.BackOff;
/** /**
@ -22,6 +21,9 @@ public class MockBackOff implements BackOff {
/** Number of tries so far. */ /** Number of tries so far. */
private int numTries; private int numTries;
public MockBackOff() {
}
@Override @Override
public void reset() { public void reset() {
numTries = 0; numTries = 0;

View file

@ -10,6 +10,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class ParameterTest { public class ParameterTest {
public ParameterTest() {
}
@Test @Test
public void testGetParametersWithSharedUrl() { public void testGetParametersWithSharedUrl() {
URL url = URL.from("http://example.com"); URL url = URL.from("http://example.com");

View file

@ -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.Attributes;
import org.xbib.net.Resource; 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.resource.HtmlTemplateResourceHandler;
import org.xbib.net.http.server.route.HttpRouterContext; import org.xbib.net.http.server.route.HttpRouterContext;
import org.xbib.net.util.ExceptionFormatter; import org.xbib.net.util.ExceptionFormatter;

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -1,21 +1,14 @@
package org.xbib.net.http.j2html; package org.xbib.net.http.j2html;
import org.xbib.net.Resource; 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.resource.HtmlTemplateResourceHandler;
import org.xbib.net.http.server.route.HttpRouterContext; import org.xbib.net.http.server.route.HttpRouterContext;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Objects;
import static org.xbib.j2html.TagCreator.body; import static org.xbib.j2html.TagCreator.body;
import static org.xbib.j2html.TagCreator.h1; import static org.xbib.j2html.TagCreator.h1;
import static org.xbib.net.http.HttpHeaderNames.CONTENT_TYPE;
public class J2HtmlResourceHandler extends HtmlTemplateResourceHandler { public class J2HtmlResourceHandler extends HtmlTemplateResourceHandler {
@ -31,43 +24,4 @@ public class J2HtmlResourceHandler extends HtmlTemplateResourceHandler {
protected Resource createResource(HttpRouterContext httpRouterContext) throws IOException { protected Resource createResource(HttpRouterContext httpRouterContext) throws IOException {
return new J2HtmlResource(this, httpRouterContext); 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();
}
}
} }

View file

@ -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.route.HttpRouter;
import org.xbib.net.http.server.service.HttpService; import org.xbib.net.http.server.service.HttpService;
import org.xbib.net.http.server.auth.BasicAuthenticationHandler; 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.LdapContextFactory;
import org.xbib.net.http.server.ldap.LdapGroupMapping; import org.xbib.net.http.server.ldap.LdapGroupMapping;
import org.xbib.net.http.server.ldap.LdapRealm; 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.HttpsRequest;
import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig; import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig;
import org.xbib.net.http.server.resource.ClassLoaderResourceHandler; 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.GroovyInternalServerErrorHandler;
import org.xbib.net.http.template.groovy.GroovyHttpStatusHandler; import org.xbib.net.http.template.groovy.GroovyHttpStatusHandler;
import org.xbib.net.http.template.groovy.GroovyTemplateResourceHandler; import org.xbib.net.http.template.groovy.GroovyTemplateResourceHandler;
@ -125,9 +125,9 @@ public final class Bootstrap {
BasicAuthenticationHandler basicAuthenticationHandler = BasicAuthenticationHandler basicAuthenticationHandler =
new BasicAuthenticationHandler(ldapRealm); new BasicAuthenticationHandler(ldapRealm);
FormAuthenticationHandler formAuthenticationHandler = GroovyFormAuthenticationHandler formAuthenticationHandler =
new FormAuthenticationHandler("j_username", "j_password", "j_remember", new GroovyFormAuthenticationHandler("j_username", "j_password",
"demo/auth/form/index.gtpl", ldapRealm); ldapRealm, "demo/auth/form/index.gtpl");
HttpSecurityDomain securityDomain = BaseHttpSecurityDomain.builder() HttpSecurityDomain securityDomain = BaseHttpSecurityDomain.builder()
.setSecurityRealm(ldapRealm) .setSecurityRealm(ldapRealm)

View file

@ -5,11 +5,14 @@ import org.xbib.config.ConfigLogger;
import org.xbib.config.ConfigParams; import org.xbib.config.ConfigParams;
import org.xbib.config.SystemConfigLogger; import org.xbib.config.SystemConfigLogger;
import org.xbib.net.NetworkClass; import org.xbib.net.NetworkClass;
import org.xbib.net.Resource;
import org.xbib.net.http.HttpHeaderNames; import org.xbib.net.http.HttpHeaderNames;
import org.xbib.net.http.HttpHeaderValues; import org.xbib.net.http.HttpHeaderValues;
import org.xbib.net.http.HttpResponseStatus; import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.HttpVersion; 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.j2html.J2HtmlService;
import org.xbib.net.http.server.application.web.WebApplication; import org.xbib.net.http.server.application.web.WebApplication;
import org.xbib.net.http.server.auth.BasicAuthenticationHandler; 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.HttpsRequest;
import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig; import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig;
import org.xbib.net.http.server.resource.ClassLoaderResourceHandler; 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.BaseHttpRouter;
import org.xbib.net.http.server.route.HttpRouter; 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.BaseHttpService;
import org.xbib.net.http.server.service.HttpService;
import org.xbib.settings.Settings; import org.xbib.settings.Settings;
import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -42,6 +47,9 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import static org.xbib.j2html.TagCreator.body;
import static org.xbib.j2html.TagCreator.h1;
public final class Bootstrap { public final class Bootstrap {
private static final ConfigLogger bootLogger; private static final ConfigLogger bootLogger;
@ -125,32 +133,13 @@ public final class Bootstrap {
new BasicAuthenticationHandler(ldapRealm); new BasicAuthenticationHandler(ldapRealm);
FormAuthenticationHandler formAuthenticationHandler = FormAuthenticationHandler formAuthenticationHandler =
new FormAuthenticationHandler("j_username", "j_password", "j_remember", new FormAuthenticationHandler("_username", "_password", ldapRealm);
"demo/auth/form/index.gtpl", ldapRealm);
HttpSecurityDomain securityDomain = BaseHttpSecurityDomain.builder() HttpSecurityDomain securityDomain = BaseHttpSecurityDomain.builder()
.setSecurityRealm(ldapRealm) .setSecurityRealm(ldapRealm)
.setHandlers(formAuthenticationHandler) .setHandlers(formAuthenticationHandler)
.build(); .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() HttpRouter httpRouter = BaseHttpRouter.builder()
.setHandler(500, new InternalServerErrorHandler()) .setHandler(500, new InternalServerErrorHandler())
.addDomain(BaseHttpDomain.builder() .addDomain(BaseHttpDomain.builder()
@ -168,10 +157,27 @@ public final class Bootstrap {
.setPath("/webjars/**") .setPath("/webjars/**")
.setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/")) .setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/"))
.build()) .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() .addService(J2HtmlService.builder()
.setPath("glob:**") .setPath("glob:**")
.setHandler(new J2HtmlResourceHandler()) .setSecurityDomain(securityDomain)
.setHandler(new MyResourceHandler())
.build()) .build())
.build()) .build())
.build(); .build();
@ -197,6 +203,47 @@ public final class Bootstrap {
return 0; 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 = private static final String hexFavIcon =
"000001000100101000000100200068040000160000002800000010000000" + "000001000100101000000100200068040000160000002800000010000000" +
"200000000100200000000000000000000000000000000000000000000000" + "200000000100200000000000000000000000000000000000000000000000" +

View file

@ -26,6 +26,9 @@ public class SimpleHttpsServerTest {
private static final Logger logger = Logger.getLogger(SimpleHttpsServerTest.class.getName()); private static final Logger logger = Logger.getLogger(SimpleHttpsServerTest.class.getName());
public SimpleHttpsServerTest() {
}
@Test @Test
public void simpleSecureHttpsServerTest() throws Exception { public void simpleSecureHttpsServerTest() throws Exception {
HttpsAddress httpsAddress = HttpsAddress.builder() HttpsAddress httpsAddress = HttpsAddress.builder()

View file

@ -1,5 +1,6 @@
package org.xbib.net.http.server.application; package org.xbib.net.http.server.application;
@FunctionalInterface
public interface Resolver<R> { public interface Resolver<R> {
R resolve(String string); R resolve(String string);

View file

@ -50,6 +50,6 @@ public class BasicAuthenticationHandler extends LoginAuthenticationHandler imple
} }
logger.log(Level.INFO, "unauthenticated"); logger.log(Level.INFO, "unauthenticated");
context.status(HttpResponseStatus.UNAUTHORIZED) context.status(HttpResponseStatus.UNAUTHORIZED)
.header("WWW-Authenticate", "Basic realm=\"" + securityRealm.getName() + "\""); .header("WWW-Authenticate", "Basic realm=\"" + getSecurityRealm().getName() + "\"");
} }
} }

View file

@ -15,73 +15,60 @@ public class FormAuthenticationHandler extends LoginAuthenticationHandler implem
private static final Logger logger = Logger.getLogger(FormAuthenticationHandler.class.getName()); private static final Logger logger = Logger.getLogger(FormAuthenticationHandler.class.getName());
String usernameParameter;
String passwordParameter;
String rememberParameter;
String loginPage;
public FormAuthenticationHandler(String usernameParameter, public FormAuthenticationHandler(String usernameParameter,
String passwordParameter, String passwordParameter,
String rememberParameter,
String loginPage,
SecurityRealm securityRealm) { SecurityRealm securityRealm) {
super(usernameParameter, passwordParameter, securityRealm); super(usernameParameter, passwordParameter, securityRealm);
this.usernameParameter = usernameParameter;
this.passwordParameter = passwordParameter;
this.rememberParameter = rememberParameter;
this.loginPage = loginPage;
} }
@Override @Override
public void handle(HttpRouterContext context) throws IOException { 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"); UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile");
if (userProfile != null && userProfile.getUserId() != null) { if (userProfile != null && userProfile.getUserId() != null) {
logger.log(Level.FINE, "user id already set: " + userProfile.getUserId()); logger.log(Level.FINE, "user id already set: " + userProfile.getUserId());
return; 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(); userProfile = new BaseUserProfile();
context.getAttributes().put("userprofile", userProfile); context.getAttributes().put("userprofile", userProfile);
Parameter parameter = context.getRequest().getParameter(); boolean isAuthenticated = false;
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;
}
try { try {
String username = parameter.getAsString(usernameParameter, Parameter.Domain.FORM); Parameter parameter = context.getRequest().getParameter();
String password = parameter.getAsString(passwordParameter, Parameter.Domain.FORM); if (!parameter.containsKey(getUserParameterName(), Parameter.Domain.FORM)) {
logger.log(Level.FINE, "username and password found, ready for authentication"); 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()); authenticate(userProfile, username, password, context.getRequest());
logger.log(Level.FINE, "successful authentication"); isAuthenticated = true;
return;
} catch (ParameterException e) { } catch (ParameterException e) {
logger.log(Level.SEVERE, "parameter error"); logger.log(Level.SEVERE, "parameter error");
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "authentication error"); logger.log(Level.SEVERE, "authentication error");
} finally {
context.setAuthenticated(isAuthenticated);
if (!isAuthenticated) {
prepareFormAuthentication(context);
}
} }
prepareFormAuthentication(context);
} }
private void prepareFormAuthentication(HttpRouterContext context) { protected void prepareFormAuthentication(HttpRouterContext context) {
// this will redirect internally to login page, and back to the original path. URL origin = context.getContextURL().resolve(context.getRequest().getRequestURI()).normalize();
// We need a full path resolve against the server URL. logger.log(Level.FINE, "context URL = " + context.getContextURL() +
logger.log(Level.FINE, "templatePath = " + loginPage); " request URI = " + context.getRequest().getRequestURI() +
context.getAttributes().put("templatePath", loginPage); " origin = " + origin);
URL loc = context.getContextURL().resolve(context.getRequest().getRequestURI()).normalize(); context.getAttributes().put("_origin", origin.toExternalForm());
logger.log(Level.FINE, "context URL = " + context.getContextURL() + " request URI = " + context.getRequest().getRequestURI() + " loc = " + loc);
context.getAttributes().put("originalPath", loc.toExternalForm());
} }
} }

View file

@ -7,6 +7,7 @@ import java.util.logging.Logger;
import org.xbib.net.Authenticator; import org.xbib.net.Authenticator;
import org.xbib.net.GroupsProvider; import org.xbib.net.GroupsProvider;
import org.xbib.net.Parameter; import org.xbib.net.Parameter;
import org.xbib.net.ParameterException;
import org.xbib.net.Request; import org.xbib.net.Request;
import org.xbib.net.SecurityRealm; import org.xbib.net.SecurityRealm;
import org.xbib.net.UserDetails; import org.xbib.net.UserDetails;
@ -23,7 +24,7 @@ public class LoginAuthenticationHandler implements HttpHandler {
private final String passwordParameterName; private final String passwordParameterName;
protected final SecurityRealm securityRealm; private final SecurityRealm securityRealm;
public LoginAuthenticationHandler(String userParameterName, public LoginAuthenticationHandler(String userParameterName,
String passwordParameterName, String passwordParameterName,
@ -33,21 +34,39 @@ public class LoginAuthenticationHandler implements HttpHandler {
this.securityRealm = securityRealm; this.securityRealm = securityRealm;
} }
public String getUserParameterName() {
return userParameterName;
}
public String getPasswordParameterName() {
return passwordParameterName;
}
public SecurityRealm getSecurityRealm() {
return securityRealm;
}
@Override @Override
public void handle(HttpRouterContext context) throws IOException { public void handle(HttpRouterContext context) throws IOException {
UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile"); UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile");
if (userProfile != null && userProfile.getUserId() != null) { if (userProfile != null && userProfile.getUserId() != null) {
return; return;
} }
Parameter parameter = context.getRequest().getParameter();
userProfile = new BaseUserProfile(); userProfile = new BaseUserProfile();
context.getAttributes().put("userprofile", userProfile);
boolean isAuthenticated = false;
try { try {
authenticate(userProfile, String username = parameter.getAsString(getUserParameterName(), Parameter.Domain.FORM);
(String) context.getRequest().getParameter().get(userParameterName, Parameter.Domain.FORM), String password = parameter.getAsString(getPasswordParameterName(), Parameter.Domain.FORM);
(String) context.getRequest().getParameter().get(passwordParameterName, Parameter.Domain.FORM), authenticate(userProfile, username, password, context.getRequest());
context.getRequest()); isAuthenticated = true;
context.getAttributes().put("userprofile", userProfile); } catch (ParameterException e) {
logger.log(Level.SEVERE, "parameter error");
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "authentication error"); logger.log(Level.SEVERE, "authentication error");
} finally {
context.setAuthenticated(isAuthenticated);
} }
} }

View file

@ -57,6 +57,8 @@ public class BaseHttpRouterContext implements HttpRouterContext {
private boolean next; private boolean next;
private boolean isAuthenticated;
public BaseHttpRouterContext(Application application, public BaseHttpRouterContext(Application application,
HttpDomain domain, HttpDomain domain,
HttpRequestBuilder httpRequestBuilder, HttpRequestBuilder httpRequestBuilder,
@ -280,6 +282,15 @@ public class BaseHttpRouterContext implements HttpRouterContext {
return httpResponseBuilder.getLength(); return httpResponseBuilder.getLength();
} }
public void setAuthenticated(boolean isAuthenticated) {
this.isAuthenticated = isAuthenticated;
}
@Override
public boolean isAuthenticated() {
return isAuthenticated;
}
@Override @Override
public void flush() throws IOException { public void flush() throws IOException {
httpResponseBuilder.build().flush(); httpResponseBuilder.build().flush();

View file

@ -97,6 +97,10 @@ public interface HttpRouterContext {
long lengthInBytes(); long lengthInBytes();
void setAuthenticated(boolean isAuthenticated);
boolean isAuthenticated();
void flush() throws IOException; void flush() throws IOException;
void close() throws IOException; void close() throws IOException;

View file

@ -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());
}
}

View file

@ -50,7 +50,7 @@ public class GroovyTemplateServiceBuilder extends BaseHttpServiceBuilder {
} }
@Override @Override
public BaseHttpServiceBuilder setSecurityDomain(HttpSecurityDomain securityDomain) { public GroovyTemplateServiceBuilder setSecurityDomain(HttpSecurityDomain securityDomain) {
super.setSecurityDomain(securityDomain); super.setSecurityDomain(securityDomain);
return this; return this;
} }