From 1d7058b30bd990cda7d4efc1c029501f05a07113 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sun, 17 Jul 2022 16:48:24 +0200 Subject: [PATCH] JDK HTTP server authentication --- .../src/org/argeo/api/cms/CmsState.java | 2 -- .../httpserver/HttpContextServlet.java | 14 +++++++--- .../OSGI-INF/jettyServiceFactory.xml | 4 +++ .../org/argeo/cms/jetty/CmsJettyServer.java | 15 +++++++++++ .../org/argeo/cms/jetty/JettyHttpContext.java | 6 +++++ org.argeo.cms/OSGI-INF/cmsAcrHttpHandler.xml | 8 ++++++ org.argeo.cms/OSGI-INF/cmsAuthenticator.xml | 8 ++++++ org.argeo.cms/OSGI-INF/cmsDeployment.xml | 2 ++ org.argeo.cms/bnd.bnd | 2 ++ org.argeo.cms/build.properties | 4 ++- .../cms/internal/http/CmsAuthenticator.java | 2 +- .../internal/http/RemoteAuthHttpExchange.java | 12 +++++---- .../internal/runtime/CmsAcrHttpHandler.java | 7 +++++ .../internal/runtime/CmsDeploymentImpl.java | 27 +++++++++++++++++++ .../cms/internal/runtime/CmsStateImpl.java | 16 +++++------ .../org/argeo/util/dav/DavServerHandler.java | 4 +++ .../src/org/argeo/util/http/HttpMethod.java | 6 +++++ .../e4/maintenance/DeploymentEntryPoint.java | 5 ++-- 18 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 org.argeo.cms/OSGI-INF/cmsAcrHttpHandler.xml create mode 100644 org.argeo.cms/OSGI-INF/cmsAuthenticator.xml create mode 100644 org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java create mode 100644 org.argeo.util/src/org/argeo/util/http/HttpMethod.java diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/CmsState.java b/org.argeo.api.cms/src/org/argeo/api/cms/CmsState.java index 181e4b9c6..9ffba68c1 100644 --- a/org.argeo.api.cms/src/org/argeo/api/cms/CmsState.java +++ b/org.argeo.api.cms/src/org/argeo/api/cms/CmsState.java @@ -6,8 +6,6 @@ import java.util.UUID; /** A running node process. */ public interface CmsState { - String getHostname(); - Long getAvailableSince(); UUID getUuid(); diff --git a/org.argeo.cms.ee/src/org/argeo/cms/servlet/httpserver/HttpContextServlet.java b/org.argeo.cms.ee/src/org/argeo/cms/servlet/httpserver/HttpContextServlet.java index c81bad7bc..b2f739449 100644 --- a/org.argeo.cms.ee/src/org/argeo/cms/servlet/httpserver/HttpContextServlet.java +++ b/org.argeo.cms.ee/src/org/argeo/cms/servlet/httpserver/HttpContextServlet.java @@ -7,6 +7,9 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.argeo.cms.auth.RemoteAuthSession; +import org.argeo.cms.servlet.ServletHttpSession; + import com.sun.net.httpserver.Authenticator; import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpHandler; @@ -24,6 +27,8 @@ public class HttpContextServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try (ServletHttpExchange httpExchange = new ServletHttpExchange(httpContext, req, resp)) { + ServletHttpSession httpSession = new ServletHttpSession(req.getSession()); + httpExchange.setAttribute(RemoteAuthSession.class.getName(), httpSession); Authenticator authenticator = httpContext.getAuthenticator(); if (authenticator != null) { Authenticator.Result authenticationResult = authenticator.authenticate(httpExchange); @@ -31,10 +36,14 @@ public class HttpContextServlet extends HttpServlet { HttpPrincipal httpPrincipal = ((Authenticator.Success) authenticationResult).getPrincipal(); httpExchange.setPrincipal(httpPrincipal); } else if (authenticationResult instanceof Authenticator.Retry) { - resp.setStatus(((Authenticator.Retry) authenticationResult).getResponseCode()); + httpExchange.sendResponseHeaders((((Authenticator.Retry) authenticationResult).getResponseCode()), + -1); + resp.flushBuffer(); return; } else if (authenticationResult instanceof Authenticator.Failure) { - resp.setStatus(((Authenticator.Failure) authenticationResult).getResponseCode()); + httpExchange.sendResponseHeaders(((Authenticator.Failure) authenticationResult).getResponseCode(), + -1); + resp.flushBuffer(); return; } else { throw new UnsupportedOperationException( @@ -46,5 +55,4 @@ public class HttpContextServlet extends HttpServlet { httpHandler.handle(httpExchange); } } - } diff --git a/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml b/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml index 2eec733bc..e2c23f85d 100644 --- a/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml +++ b/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml @@ -3,4 +3,8 @@ + + + + diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/CmsJettyServer.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/CmsJettyServer.java index 7f33beeb6..3b9783ef5 100644 --- a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/CmsJettyServer.java +++ b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/CmsJettyServer.java @@ -9,6 +9,8 @@ import javax.servlet.ServletException; import javax.websocket.DeploymentException; import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpointConfig; +import com.sun.net.httpserver.Authenticator; +import com.sun.net.httpserver.HttpContext; import org.argeo.api.cms.CmsState; import org.argeo.cms.CmsDeployProperty; @@ -38,6 +40,8 @@ public class CmsJettyServer extends JettyHttpServer { private CmsState cmsState; + private Authenticator defaultAuthenticator; + protected void addServlets(ServletContextHandler servletContextHandler) throws ServletException { } @@ -73,6 +77,13 @@ public class CmsJettyServer extends JettyHttpServer { } + @Override + public synchronized HttpContext createContext(String path) { + HttpContext httpContext = super.createContext(path); + httpContext.setAuthenticator(defaultAuthenticator); + return httpContext; + } + protected void enableWebSocket(ServletContextHandler servletContextHandler) { String webSocketEnabled = getDeployProperty(CmsDeployProperty.WEBSOCKET_ENABLED); // web socket @@ -107,4 +118,8 @@ public class CmsJettyServer extends JettyHttpServer { this.cmsState = cmsState; } + public void setDefaultAuthenticator(Authenticator defaultAuthenticator) { + this.defaultAuthenticator = defaultAuthenticator; + } + } diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpContext.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpContext.java index 2aa4abc43..7adb09be3 100644 --- a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpContext.java +++ b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpContext.java @@ -12,6 +12,7 @@ import java.util.Set; import org.argeo.cms.servlet.httpserver.HttpContextServlet; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -36,10 +37,15 @@ class JettyHttpContext extends HttpContext { this.httpServer = httpServer; this.path = path; + // Jetty context handler ServletContextHandler servletContextHandler = new ServletContextHandler(); servletContextHandler.setContextPath(path); HttpContextServlet servlet = new HttpContextServlet(this); servletContextHandler.addServlet(new ServletHolder(servlet), "/*"); + SessionHandler sessionHandler = new SessionHandler(); + // FIXME find a better default + sessionHandler.setMaxInactiveInterval(-1); + servletContextHandler.setSessionHandler(sessionHandler); contextHandler = servletContextHandler; attributes = new ContextAttributes(); diff --git a/org.argeo.cms/OSGI-INF/cmsAcrHttpHandler.xml b/org.argeo.cms/OSGI-INF/cmsAcrHttpHandler.xml new file mode 100644 index 000000000..0a2684a3d --- /dev/null +++ b/org.argeo.cms/OSGI-INF/cmsAcrHttpHandler.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.argeo.cms/OSGI-INF/cmsAuthenticator.xml b/org.argeo.cms/OSGI-INF/cmsAuthenticator.xml new file mode 100644 index 000000000..9b152147e --- /dev/null +++ b/org.argeo.cms/OSGI-INF/cmsAuthenticator.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.argeo.cms/OSGI-INF/cmsDeployment.xml b/org.argeo.cms/OSGI-INF/cmsDeployment.xml index 8495daad7..102ae2adc 100644 --- a/org.argeo.cms/OSGI-INF/cmsDeployment.xml +++ b/org.argeo.cms/OSGI-INF/cmsDeployment.xml @@ -2,6 +2,8 @@ + + diff --git a/org.argeo.cms/bnd.bnd b/org.argeo.cms/bnd.bnd index 12114c2a8..bb8ed76bf 100644 --- a/org.argeo.cms/bnd.bnd +++ b/org.argeo.cms/bnd.bnd @@ -11,8 +11,10 @@ OSGI-INF/cmsState.xml,\ OSGI-INF/simpleTransactionManager.xml,\ OSGI-INF/nodeUserAdmin.xml,\ OSGI-INF/cmsUserManager.xml,\ +OSGI-INF/cmsAuthenticator.xml,\ OSGI-INF/uuidFactory.xml,\ OSGI-INF/acrContentRepository.xml,\ +OSGI-INF/cmsAcrHttpHandler.xml,\ OSGI-INF/cmsDeployment.xml,\ OSGI-INF/cmsContext.xml,\ diff --git a/org.argeo.cms/build.properties b/org.argeo.cms/build.properties index 67b98f4d0..11ab9e06e 100644 --- a/org.argeo.cms/build.properties +++ b/org.argeo.cms/build.properties @@ -2,5 +2,7 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ bin/,\ - OSGI-INF/ + OSGI-INF/,\ + OSGI-INF/cmsAuthenticator.xml,\ + OSGI-INF/cmsAcrHttpHandler.xml source.. = src/ diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/CmsAuthenticator.java b/org.argeo.cms/src/org/argeo/cms/internal/http/CmsAuthenticator.java index 54849fc03..04312eca3 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/CmsAuthenticator.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/CmsAuthenticator.java @@ -69,7 +69,7 @@ public class CmsAuthenticator extends Authenticator { } protected boolean authIsRequired(HttpExchange httpExchange) { - return false; + return true; } protected LoginContext processUnauthorized(HttpExchange httpExchange) { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/RemoteAuthHttpExchange.java b/org.argeo.cms/src/org/argeo/cms/internal/http/RemoteAuthHttpExchange.java index 68e0b1e3e..00f2b8fe1 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/RemoteAuthHttpExchange.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/RemoteAuthHttpExchange.java @@ -3,6 +3,7 @@ package org.argeo.cms.internal.http; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Objects; import org.argeo.cms.auth.RemoteAuthRequest; import org.argeo.cms.auth.RemoteAuthResponse; @@ -11,10 +12,13 @@ import org.argeo.cms.auth.RemoteAuthSession; import com.sun.net.httpserver.HttpExchange; public class RemoteAuthHttpExchange implements RemoteAuthRequest, RemoteAuthResponse { - private HttpExchange httpExchange; + private final HttpExchange httpExchange; + private RemoteAuthSession remoteAuthSession; public RemoteAuthHttpExchange(HttpExchange httpExchange) { this.httpExchange = httpExchange; + this.remoteAuthSession = (RemoteAuthSession) httpExchange.getAttribute(RemoteAuthSession.class.getName()); + Objects.requireNonNull(this.remoteAuthSession); } @Override @@ -24,14 +28,12 @@ public class RemoteAuthHttpExchange implements RemoteAuthRequest, RemoteAuthResp @Override public RemoteAuthSession getSession() { - // TODO Auto-generated method stub - return null; + return remoteAuthSession; } @Override public RemoteAuthSession createSession() { - // TODO Auto-generated method stub - return null; + throw new UnsupportedOperationException("Cannot create remote session"); } @Override diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java new file mode 100644 index 000000000..d9fa9e751 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java @@ -0,0 +1,7 @@ +package org.argeo.cms.internal.runtime; + +import org.argeo.util.dav.DavServerHandler; + +public class CmsAcrHttpHandler extends DavServerHandler { + +} diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java index 1b94bdad1..8b0fbbc68 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java @@ -1,10 +1,15 @@ package org.argeo.cms.internal.runtime; +import java.util.Map; + import org.argeo.api.cms.CmsDeployment; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsState; import org.argeo.cms.CmsDeployProperty; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + /** Implementation of a CMS deployment. */ public class CmsDeploymentImpl implements CmsDeployment { private final CmsLog log = CmsLog.getLog(getClass()); @@ -16,6 +21,8 @@ public class CmsDeploymentImpl implements CmsDeployment { private CmsState cmsState; // private DeployConfig deployConfig; + private HttpServer httpServer; + public void start() { // httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null; // if (deployConfig.hasDomain()) { @@ -69,6 +76,26 @@ public class CmsDeploymentImpl implements CmsDeployment { httpExpected = httpPort != null || httpsPort != null; } + public void setHttpServer(HttpServer httpServer) { + this.httpServer = httpServer; + } + + public void addHttpHandler(HttpHandler httpHandler, Map properties) { + if (httpServer == null) + return; + final String contextPath = properties.get("contextPath"); + httpServer.createContext(contextPath, httpHandler); + log.debug(() -> "Added handler " + contextPath + " : " + httpHandler.getClass().getName()); + + } + + public void removeHttpHandler(HttpHandler httpHandler, Map properties) { + if (httpServer == null) + return; + final String contextPath = properties.get("contextPath"); + httpServer.removeContext(contextPath); + log.debug(() -> "Removed handler " + contextPath + " : " + httpHandler.getClass().getName()); + } // public void setHttpService(HttpService httpService) { // this.httpService = httpService; // } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java index dff4d85df..7252f425f 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java @@ -44,7 +44,7 @@ public class CmsStateImpl implements CmsState { private UUID uuid; // private final boolean cleanState; - private String hostname; +// private String hostname; private UuidFactory uuidFactory; @@ -83,11 +83,11 @@ public class CmsStateImpl implements CmsState { // this.uuid = UUID.fromString(stateUuidStr); this.uuid = uuidFactory.timeUUID(); // this.cleanState = stateUuid.equals(frameworkUuid); - try { - this.hostname = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - log.error("Cannot set hostname: " + e); - } +// try { +// this.hostname = InetAddress.getLocalHost().getHostName(); +// } catch (UnknownHostException e) { +// log.error("Cannot set hostname: " + e); +// } availableSince = System.currentTimeMillis(); if (log.isDebugEnabled()) { @@ -337,10 +337,6 @@ public class CmsStateImpl implements CmsState { /* * ACCESSORS */ - public String getHostname() { - return hostname; - } - @Override public UUID getUuid() { return uuid; diff --git a/org.argeo.util/src/org/argeo/util/dav/DavServerHandler.java b/org.argeo.util/src/org/argeo/util/dav/DavServerHandler.java index 4fc73f3ce..f9fba4761 100644 --- a/org.argeo.util/src/org/argeo/util/dav/DavServerHandler.java +++ b/org.argeo.util/src/org/argeo/util/dav/DavServerHandler.java @@ -2,6 +2,8 @@ package org.argeo.util.dav; import java.io.IOException; +import org.argeo.util.http.HttpMethod; + import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; @@ -12,6 +14,8 @@ public class DavServerHandler implements HttpHandler { String method = exchange.getRequestMethod(); if (DavMethod.PROPFIND.name().equals(method)) { handle(exchange); + } else if (HttpMethod.GET.name().equals(method)) { + exchange.getResponseBody().write("Hello Dav!".getBytes()); } else { throw new IllegalArgumentException("Unsupported method " + method); } diff --git a/org.argeo.util/src/org/argeo/util/http/HttpMethod.java b/org.argeo.util/src/org/argeo/util/http/HttpMethod.java new file mode 100644 index 000000000..2116057a4 --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/http/HttpMethod.java @@ -0,0 +1,6 @@ +package org.argeo.util.http; + +public enum HttpMethod { + GET,// + ; +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java index e713f53e1..0a28dc5ee 100644 --- a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java @@ -71,15 +71,14 @@ class DeploymentEntryPoint { Label label = new Label(composite, SWT.WRAP); CmsSwtUtils.markup(label); if (nodeDeploymentRef == null) { - label.setText("Not yet deployed on
" + nodeState.getHostname() + "
, please configure below."); + label.setText("Not yet deployed on, please configure below."); } else { Object stateUuid = nodeStateRef.getProperty(CmsConstants.CN); CmsContext nodeDeployment = bc.getService(nodeDeploymentRef); GregorianCalendar calendar = new GregorianCalendar(); calendar.setTimeInMillis(nodeDeployment.getAvailableSince()); calendar.setTimeZone(TimeZone.getDefault()); - label.setText("[" + "" + nodeState.getHostname() + "]# " + "Deployment state " + stateUuid - + ", available since " + calendar.getTime() + ""); + label.setText("Deployment state " + stateUuid + ", available since " + calendar.getTime() + ""); } } -- 2.30.2