From 921b6cf95420aafa6b9cebe107c927e8062ed865 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 21 Jul 2022 09:02:53 +0200 Subject: [PATCH] Improve CMS start up and status reporting. --- .../src/org/argeo/api/cms/CmsConstants.java | 1 + org.argeo.cms.ee/OSGI-INF/statusHandler.xml | 3 +- .../server/CmsWebSocketConfigurator.java | 10 +- .../cms/websocket/server/PingEndpoint.java | 16 ++++ .../server/PublicWebSocketConfigurator.java | 13 +++ .../cms/websocket/server/StatusEndpoints.java | 26 ------ .../cms/websocket/server/StatusHandler.java | 55 +++++++++++ .../OSGI-INF/jettyServiceFactory.xml | 1 - .../org/argeo/cms/jetty/CmsJettyServer.java | 20 ++-- .../OSGI-INF/cmsSshServer.xml | 3 + .../src/org/argeo/cms/ssh/CmsSshServer.java | 6 +- org.argeo.cms/OSGI-INF/cmsDeployment.xml | 3 +- org.argeo.cms/bnd.bnd | 1 - org.argeo.cms/src/org/argeo/cms/CmsSshd.java | 6 ++ .../src/org/argeo/cms/auth/CmsAuthUtils.java | 3 +- .../org/argeo/cms/auth/RemoteAuthUtils.java | 2 +- .../cms/client/WebSocketEventClient.java | 1 + .../src/org/argeo/cms/client/WsPing.java | 86 +++++++++++++++++ .../internal/auth/RemoteCmsSessionImpl.java | 5 +- .../http/AbstractHttpAuthenticator.java | 18 ---- .../cms/internal/http/CmsAuthenticator.java | 3 +- .../internal/http/PublicCmsAuthenticator.java | 13 +++ .../cms/internal/runtime/CmsContextImpl.java | 25 ++++- .../internal/runtime/CmsDeploymentImpl.java | 92 +++++++++---------- 24 files changed, 286 insertions(+), 126 deletions(-) create mode 100644 org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PingEndpoint.java create mode 100644 org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PublicWebSocketConfigurator.java delete mode 100644 org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusEndpoints.java create mode 100644 org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusHandler.java create mode 100644 org.argeo.cms/src/org/argeo/cms/CmsSshd.java create mode 100644 org.argeo.cms/src/org/argeo/cms/client/WsPing.java delete mode 100644 org.argeo.cms/src/org/argeo/cms/internal/http/AbstractHttpAuthenticator.java create mode 100644 org.argeo.cms/src/org/argeo/cms/internal/http/PublicCmsAuthenticator.java diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/CmsConstants.java b/org.argeo.api.cms/src/org/argeo/api/cms/CmsConstants.java index 98364d97b..34d85a252 100644 --- a/org.argeo.api.cms/src/org/argeo/api/cms/CmsConstants.java +++ b/org.argeo.api.cms/src/org/argeo/api/cms/CmsConstants.java @@ -83,6 +83,7 @@ public interface CmsConstants { * COMPONENT PROPERTIES */ String CONTEXT_PATH = "context.path"; + String CONTEXT_PUBLIC = "context.public"; String EVENT_TOPICS = "event.topics"; /* diff --git a/org.argeo.cms.ee/OSGI-INF/statusHandler.xml b/org.argeo.cms.ee/OSGI-INF/statusHandler.xml index a530c336a..8461cff1f 100644 --- a/org.argeo.cms.ee/OSGI-INF/statusHandler.xml +++ b/org.argeo.cms.ee/OSGI-INF/statusHandler.xml @@ -1,8 +1,9 @@ - + + diff --git a/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/CmsWebSocketConfigurator.java b/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/CmsWebSocketConfigurator.java index 279b610b6..0ded176f4 100644 --- a/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/CmsWebSocketConfigurator.java +++ b/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/CmsWebSocketConfigurator.java @@ -27,11 +27,11 @@ import org.argeo.cms.servlet.CmsServletContext; * the initialisation of a new web socket. */ public class CmsWebSocketConfigurator extends Configurator { - public final static String WEBSOCKET_SUBJECT = "org.argeo.cms.websocket.subject"; - public final static String REMOTE_USER = "org.osgi.service.http.authentication.remote.user"; +// public final static String WEBSOCKET_SUBJECT = "org.argeo.cms.websocket.subject"; +// public final static String REMOTE_USER = "org.osgi.service.http.authentication.remote.user"; private final static CmsLog log = CmsLog.getLog(CmsWebSocketConfigurator.class); - final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; +// final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; @Override public boolean checkOrigin(String originHeaderValue) { @@ -98,8 +98,10 @@ public class CmsWebSocketConfigurator extends Configurator { } else { lc = RemoteAuthUtils.anonymousLogin(remoteAuthRequest, remoteAuthResponse); } - if (lc == null) + if (lc == null) { rejectResponse(response, e); + return; + } } finally { Thread.currentThread().setContextClassLoader(currentThreadContextClassLoader); } diff --git a/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PingEndpoint.java b/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PingEndpoint.java new file mode 100644 index 000000000..b81cc591d --- /dev/null +++ b/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PingEndpoint.java @@ -0,0 +1,16 @@ +package org.argeo.cms.websocket.server; + +import javax.websocket.OnError; +import javax.websocket.server.ServerEndpoint; + +import org.argeo.api.cms.CmsLog; + +@ServerEndpoint(value = "/ping", configurator = PublicWebSocketConfigurator.class) +public class PingEndpoint { + private final static CmsLog log = CmsLog.getLog(PingEndpoint.class); + + @OnError + public void onError(Throwable e) { + log.error("Cannot process ping", e); + } +} diff --git a/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PublicWebSocketConfigurator.java b/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PublicWebSocketConfigurator.java new file mode 100644 index 000000000..e3e17cf05 --- /dev/null +++ b/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PublicWebSocketConfigurator.java @@ -0,0 +1,13 @@ +package org.argeo.cms.websocket.server; + +import org.argeo.cms.auth.RemoteAuthRequest; +import org.argeo.cms.auth.RemoteAuthResponse; + +public class PublicWebSocketConfigurator extends CmsWebSocketConfigurator { + + @Override + protected boolean authIsRequired(RemoteAuthRequest remoteAuthRequest, RemoteAuthResponse remoteAuthResponse) { + return false; + } + +} diff --git a/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusEndpoints.java b/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusEndpoints.java deleted file mode 100644 index d5839e25e..000000000 --- a/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusEndpoints.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.argeo.cms.websocket.server; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class StatusEndpoints implements WebsocketEndpoints, HttpHandler { - - @Override - public Set> getEndPoints() { - Set> res = new HashSet<>(); - res.add(EventEndpoint.class); - res.add(TestEndpoint.class); - return res; - } - - @Override - public void handle(HttpExchange exchange) throws IOException { - // web socket only - exchange.sendResponseHeaders(200, -1); - } - -} diff --git a/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusHandler.java b/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusHandler.java new file mode 100644 index 000000000..a8466fee2 --- /dev/null +++ b/org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusHandler.java @@ -0,0 +1,55 @@ +package org.argeo.cms.websocket.server; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.StringJoiner; + +import org.argeo.api.cms.CmsState; +import org.argeo.cms.CmsDeployProperty; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class StatusHandler implements WebsocketEndpoints, HttpHandler { + private CmsState cmsState; + + @Override + public Set> getEndPoints() { + Set> res = new HashSet<>(); + res.add(PingEndpoint.class); + res.add(EventEndpoint.class); + res.add(TestEndpoint.class); + return res; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + + StringJoiner sb = new StringJoiner("\n"); + CmsDeployProperty[] deployProperties = CmsDeployProperty.values(); + Arrays.sort(deployProperties, (o1, o2) -> o1.name().compareTo(o2.name())); + for (CmsDeployProperty deployProperty : deployProperties) { + List values = cmsState.getDeployProperties(deployProperty.getProperty()); + for (int i = 0; i < values.size(); i++) { + String value = values.get(i); + if (value != null) { + String line = deployProperty.getProperty() + (i == 0 ? "" : "." + i) + "=" + value; + sb.add(line); + } + } + } + + byte[] msg = sb.toString().getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, msg.length); + exchange.getResponseBody().write(msg); + } + + public void setCmsState(CmsState cmsState) { + this.cmsState = cmsState; + } + +} diff --git a/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml b/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml index e2c23f85d..6a1336220 100644 --- a/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml +++ b/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml @@ -3,7 +3,6 @@ - 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 a18f4b495..4927e6f10 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 @@ -41,7 +41,7 @@ public class CmsJettyServer extends JettyHttpServer { private CmsState cmsState; - private Authenticator defaultAuthenticator; +// private Authenticator defaultAuthenticator; protected void addServlets(ServletContextHandler servletContextHandler) throws ServletException { } @@ -78,12 +78,12 @@ public class CmsJettyServer extends JettyHttpServer { } - @Override - public synchronized HttpContext createContext(String path) { - HttpContext httpContext = super.createContext(path); - httpContext.setAuthenticator(defaultAuthenticator); - return httpContext; - } +// @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); @@ -119,8 +119,8 @@ public class CmsJettyServer extends JettyHttpServer { this.cmsState = cmsState; } - public void setDefaultAuthenticator(Authenticator defaultAuthenticator) { - this.defaultAuthenticator = defaultAuthenticator; - } +// public void setDefaultAuthenticator(Authenticator defaultAuthenticator) { +// this.defaultAuthenticator = defaultAuthenticator; +// } } diff --git a/org.argeo.cms.lib.sshd/OSGI-INF/cmsSshServer.xml b/org.argeo.cms.lib.sshd/OSGI-INF/cmsSshServer.xml index aa2d8db2d..987b97745 100644 --- a/org.argeo.cms.lib.sshd/OSGI-INF/cmsSshServer.xml +++ b/org.argeo.cms.lib.sshd/OSGI-INF/cmsSshServer.xml @@ -2,4 +2,7 @@ + + + diff --git a/org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/CmsSshServer.java b/org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/CmsSshServer.java index 7246d603d..ab62654f2 100644 --- a/org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/CmsSshServer.java +++ b/org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/CmsSshServer.java @@ -11,7 +11,6 @@ import org.apache.sshd.common.util.net.SshdSocketAddress; import org.apache.sshd.scp.server.ScpCommandFactory; import org.apache.sshd.server.SshServer; import org.apache.sshd.server.auth.gss.GSSAuthenticator; -import org.apache.sshd.server.config.keys.DefaultAuthorizedKeysAuthenticator; import org.apache.sshd.server.forward.AcceptAllForwardingFilter; import org.apache.sshd.server.jaas.JaasPasswordAuthenticator; import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; @@ -22,8 +21,9 @@ import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsState; import org.argeo.cms.CmsDeployProperty; +import org.argeo.cms.CmsSshd; -public class CmsSshServer { +public class CmsSshServer implements CmsSshd { private final static CmsLog log = CmsLog.getLog(CmsSshServer.class); private static final String DEFAULT_SSH_HOST_KEY_PATH = CmsConstants.NODE + '/' + CmsConstants.NODE + ".ser"; @@ -94,7 +94,7 @@ public class CmsSshServer { }); // Authentication - //sshd.setPublickeyAuthenticator(new DefaultAuthorizedKeysAuthenticator(true)); + // sshd.setPublickeyAuthenticator(new DefaultAuthorizedKeysAuthenticator(true)); sshd.setPublickeyAuthenticator(null); // sshd.setKeyboardInteractiveAuthenticator(null); JaasPasswordAuthenticator jaasPasswordAuthenticator = new JaasPasswordAuthenticator(); diff --git a/org.argeo.cms/OSGI-INF/cmsDeployment.xml b/org.argeo.cms/OSGI-INF/cmsDeployment.xml index b00bc82f3..ea54fc2f9 100644 --- a/org.argeo.cms/OSGI-INF/cmsDeployment.xml +++ b/org.argeo.cms/OSGI-INF/cmsDeployment.xml @@ -2,7 +2,8 @@ - + + diff --git a/org.argeo.cms/bnd.bnd b/org.argeo.cms/bnd.bnd index bddc21f27..528afc812 100644 --- a/org.argeo.cms/bnd.bnd +++ b/org.argeo.cms/bnd.bnd @@ -13,7 +13,6 @@ OSGI-INF/uuidFactory.xml,\ OSGI-INF/transactionManager.xml,\ OSGI-INF/cmsUserAdmin.xml,\ OSGI-INF/cmsUserManager.xml,\ -OSGI-INF/cmsAuthenticator.xml,\ OSGI-INF/cmsContentRepository.xml,\ OSGI-INF/cmsAcrHttpHandler.xml,\ OSGI-INF/cmsDeployment.xml,\ diff --git a/org.argeo.cms/src/org/argeo/cms/CmsSshd.java b/org.argeo.cms/src/org/argeo/cms/CmsSshd.java new file mode 100644 index 000000000..e38bf6313 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/CmsSshd.java @@ -0,0 +1,6 @@ +package org.argeo.cms; + +/** Just a marker interface for the time being.*/ +public interface CmsSshd { + +} diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java index 6abaf71f2..396af9343 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java @@ -135,8 +135,7 @@ class CmsAuthUtils { // TODO move it to a service in order to avoid static synchronization if (request != null) { RemoteAuthSession httpSession = request.getSession(); - assert httpSession != null; - String httpSessId = httpSession.getId(); + String httpSessId = httpSession != null ? httpSession.getId() : null; boolean anonymous = authorization.getName() == null; String remoteUser = !anonymous ? authorization.getName() : CmsConstants.ROLE_ANONYMOUS; request.setAttribute(RemoteAuthRequest.REMOTE_USER, remoteUser); diff --git a/org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java index e6c425f69..1f2ea73a7 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java @@ -131,7 +131,7 @@ public class RemoteAuthUtils { // anonymous ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader(); try { - Thread.currentThread().setContextClassLoader(CmsAuthenticator.class.getClassLoader()); + Thread.currentThread().setContextClassLoader(RemoteAuthUtils.class.getClassLoader()); LoginContext lc = CmsAuth.ANONYMOUS .newLoginContext(new RemoteAuthCallbackHandler(remoteAuthRequest, remoteAuthResponse)); lc.login(); diff --git a/org.argeo.cms/src/org/argeo/cms/client/WebSocketEventClient.java b/org.argeo.cms/src/org/argeo/cms/client/WebSocketEventClient.java index 0787b0478..5834bd611 100644 --- a/org.argeo.cms/src/org/argeo/cms/client/WebSocketEventClient.java +++ b/org.argeo.cms/src/org/argeo/cms/client/WebSocketEventClient.java @@ -50,6 +50,7 @@ public class WebSocketEventClient { CompletableFuture ws = client.newWebSocketBuilder() .header(HttpHeader.AUTHORIZATION.getName(), HttpHeader.NEGOTIATE + " " + token) .buildAsync(uri, listener); + WebSocket webSocket = ws.get(); webSocket.request(Long.MAX_VALUE); diff --git a/org.argeo.cms/src/org/argeo/cms/client/WsPing.java b/org.argeo.cms/src/org/argeo/cms/client/WsPing.java new file mode 100644 index 000000000..0470b9f20 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/client/WsPing.java @@ -0,0 +1,86 @@ +package org.argeo.cms.client; + +import java.math.RoundingMode; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.WebSocket; +import java.nio.ByteBuffer; +import java.text.DecimalFormat; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; + +/** Tests connectivity to the web socket server. */ +public class WsPing implements Runnable { + private final static int PING_FRAME_SIZE = 125; + + private final URI uri; + private final UUID uuid; + + private final DecimalFormat decimalFormat; + + public WsPing(URI uri) { + this.uri = uri; + this.uuid = UUID.randomUUID(); + decimalFormat = new DecimalFormat("0.0"); + decimalFormat.setRoundingMode(RoundingMode.HALF_UP); + } + + @Override + public void run() { + try { + WebSocket.Listener listener = new WebSocket.Listener() { + + @Override + public CompletionStage onPong(WebSocket webSocket, ByteBuffer message) { + long msb = message.getLong(); + long lsb = message.getLong(); + long end = System.nanoTime(); + if (msb != uuid.getMostSignificantBits() || lsb != uuid.getLeastSignificantBits()) + return null; // ignore + long begin = message.getLong(); + double durationNs = end - begin; + double durationMs = durationNs / 1000000; + int size = message.remaining() + (3 * Long.BYTES); + System.out.println( + size + " bytes from " + uri + ": time=" + decimalFormat.format(durationMs) + " ms"); + return null; + } + + }; + + HttpClient client = HttpClient.newHttpClient(); + CompletableFuture ws = client.newWebSocketBuilder().buildAsync(uri, listener); + WebSocket webSocket = ws.get(); + webSocket.request(Long.MAX_VALUE); + + Runtime.getRuntime().addShutdownHook(new Thread(() -> webSocket.sendClose(WebSocket.NORMAL_CLOSURE, ""))); + + while (!webSocket.isInputClosed()) { + long begin = System.nanoTime(); +// ByteBuffer buffer = ByteBuffer.allocate(3 * Long.BYTES); + ByteBuffer buffer = ByteBuffer.allocate(PING_FRAME_SIZE); + buffer.putLong(uuid.getMostSignificantBits()); + buffer.putLong(uuid.getLeastSignificantBits()); + buffer.putLong(begin); + buffer.flip(); + webSocket.sendPing(buffer); + Thread.sleep(1000); + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + System.err.println("usage: java " + WsPing.class.getName() + " "); + System.exit(1); + return; + } + URI uri = URI.create(args[0]); + new WsPing(uri).run(); + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/RemoteCmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/RemoteCmsSessionImpl.java index 41ee7797b..23f5d8427 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/RemoteCmsSessionImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/RemoteCmsSessionImpl.java @@ -16,7 +16,8 @@ public class RemoteCmsSessionImpl extends CmsSessionImpl { public RemoteCmsSessionImpl(UUID uuid, Subject initialSubject, Authorization authorization, Locale locale, RemoteAuthRequest request) { - super(uuid, initialSubject, authorization, locale, request.getSession().getId()); + super(uuid, initialSubject, authorization, locale, + request.getSession() != null ? request.getSession().getId() : null); httpSession = request.getSession(); } @@ -24,6 +25,8 @@ public class RemoteCmsSessionImpl extends CmsSessionImpl { public boolean isValid() { if (isClosed()) return false; + if (httpSession == null) + return true; return httpSession.isValid(); } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/AbstractHttpAuthenticator.java b/org.argeo.cms/src/org/argeo/cms/internal/http/AbstractHttpAuthenticator.java deleted file mode 100644 index 418b0689d..000000000 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/AbstractHttpAuthenticator.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.argeo.cms.internal.http; - -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import org.argeo.api.cms.CmsAuth; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.auth.RemoteAuthCallbackHandler; -import org.argeo.cms.auth.RemoteAuthRequest; -import org.argeo.cms.auth.RemoteAuthResponse; -import org.argeo.cms.auth.SpnegoLoginModule; -import org.argeo.util.http.HttpHeader; - -public class AbstractHttpAuthenticator { - private final static CmsLog log = CmsLog.getLog(AbstractHttpAuthenticator.class); - - -} 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 61d2d3066..307f928a5 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 @@ -5,7 +5,6 @@ import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import org.argeo.api.cms.CmsAuth; -import org.argeo.api.cms.CmsLog; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.auth.RemoteAuthCallbackHandler; import org.argeo.cms.auth.RemoteAuthRequest; @@ -21,7 +20,7 @@ public class CmsAuthenticator extends Authenticator { // final static String HEADER_AUTHORIZATION = "Authorization"; // final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; - private final static CmsLog log = CmsLog.getLog(CmsAuthenticator.class); +// private final static CmsLog log = CmsLog.getLog(CmsAuthenticator.class); // TODO make it configurable private final String httpAuthRealm = "Argeo"; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/PublicCmsAuthenticator.java b/org.argeo.cms/src/org/argeo/cms/internal/http/PublicCmsAuthenticator.java new file mode 100644 index 000000000..14d79a63d --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/PublicCmsAuthenticator.java @@ -0,0 +1,13 @@ +package org.argeo.cms.internal.http; + +import org.argeo.cms.auth.RemoteAuthRequest; +import org.argeo.cms.auth.RemoteAuthResponse; + +public class PublicCmsAuthenticator extends CmsAuthenticator { + + @Override + protected boolean authIsRequired(RemoteAuthRequest remoteAuthRequest, RemoteAuthResponse remoteAuthResponse) { + return false; + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java index 7a0f3388c..940e329a3 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java @@ -91,7 +91,16 @@ public class CmsContextImpl implements CmsContext { // // }.open(); - checkReadiness(); + new Thread(() -> { + while (!checkReadiness()) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + } + }, "Check readiness").start(); + + // checkReadiness(); setInstance(this); } @@ -104,10 +113,13 @@ public class CmsContextImpl implements CmsContext { * Checks whether the deployment is available according to expectations, and * mark it as available. */ - private void checkReadiness() { + private boolean checkReadiness() { if (isAvailable()) - return; - if (cmsDeployment != null && userAdmin != null) { + return true; + if (cmsDeployment == null) + return false; + + if (((CmsDeploymentImpl) cmsDeployment).allExpectedServicesAvailable() && userAdmin != null) { String data = KernelUtils.getFrameworkProp(KernelUtils.OSGI_INSTANCE_AREA); String state = KernelUtils.getFrameworkProp(KernelUtils.OSGI_CONFIGURATION_AREA); availableSince = System.currentTimeMillis(); @@ -124,8 +136,11 @@ public class CmsContextImpl implements CmsContext { if (log.isTraceEnabled()) log.trace("Kernel initialization took " + initDuration + "ms"); tributeToFreeSoftware(initDuration); + + return true; } else { - throw new IllegalStateException("Deployment is not available"); + return false; + // throw new IllegalStateException("Deployment is not available"); } } 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 497ed923f..67daef2c8 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 @@ -10,7 +10,11 @@ import org.argeo.api.cms.CmsDeployment; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsState; import org.argeo.cms.CmsDeployProperty; +import org.argeo.cms.CmsSshd; +import org.argeo.cms.internal.http.CmsAuthenticator; +import org.argeo.cms.internal.http.PublicCmsAuthenticator; +import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; @@ -18,67 +22,36 @@ import com.sun.net.httpserver.HttpServer; public class CmsDeploymentImpl implements CmsDeployment { private final CmsLog log = CmsLog.getLog(getClass()); - // Readiness -// private HttpService httpService; - private CmsState cmsState; -// private DeployConfig deployConfig; + // Expectations private boolean httpExpected = false; + private boolean sshdExpected = false; + + // HTTP private HttpServer httpServer; private Map httpHandlers = new TreeMap<>(); + private Map httpAuthenticators = new TreeMap<>(); - public void start() { -// httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null; -// if (deployConfig.hasDomain()) { -// loadIpaJaasConfiguration(); -// } + // SSHD + private CmsSshd cmsSshd; + public void start() { log.debug(() -> "CMS deployment available"); } -// public void addFactoryDeployConfig(String factoryPid, Dictionary props) { -// deployConfig.putFactoryDeployConfig(factoryPid, props); -// deployConfig.save(); -// try { -// deployConfig.loadConfigs(); -// } catch (IOException e) { -// throw new IllegalStateException(e); -// } -// } -// -// public Dictionary getProps(String factoryPid, String cn) { -// return deployConfig.getProps(factoryPid, cn); -// } - -// public boolean isHttpAvailableOrNotExpected() { -// return (httpExpected ? httpService != null : true); -// } - -// private void loadIpaJaasConfiguration() { -// if (System.getProperty(KernelConstants.JAAS_CONFIG_PROP) == null) { -// String jaasConfig = KernelConstants.JAAS_CONFIG_IPA; -// URL url = getClass().getClassLoader().getResource(jaasConfig); -// KernelUtils.setJaasConfiguration(url); -// log.debug("Set IPA JAAS configuration."); -// } -// } - public void stop() { -// if (deployConfig != null) { -// deployConfig.save(); -// } } -// public void setDeployConfig(DeployConfig deployConfig) { -// this.deployConfig = deployConfig; -// } - public void setCmsState(CmsState cmsState) { this.cmsState = cmsState; + String httpPort = this.cmsState.getDeployProperty(CmsDeployProperty.HTTP_PORT.getProperty()); String httpsPort = this.cmsState.getDeployProperty(CmsDeployProperty.HTTPS_PORT.getProperty()); httpExpected = httpPort != null || httpsPort != null; + + String sshdPort = this.cmsState.getDeployProperty(CmsDeployProperty.SSHD_PORT.getProperty()); + sshdExpected = sshdPort != null; } public void setHttpServer(HttpServer httpServer) { @@ -86,8 +59,8 @@ public class CmsDeploymentImpl implements CmsDeployment { // create contexts whose handles had already been published for (String contextPath : httpHandlers.keySet()) { HttpHandler httpHandler = httpHandlers.get(contextPath); - httpServer.createContext(contextPath, httpHandler); - log.debug(() -> "Added handler " + contextPath + " : " + httpHandler.getClass().getName()); + CmsAuthenticator authenticator = httpAuthenticators.get(contextPath); + createHttpContext(contextPath, httpHandler, authenticator); } } @@ -97,12 +70,22 @@ public class CmsDeploymentImpl implements CmsDeployment { log.warn("Property " + CONTEXT_PATH + " not set on HTTP handler " + properties + ". Ignoring it."); return; } + boolean isPublic = Boolean.parseBoolean(properties.get(CmsConstants.CONTEXT_PUBLIC)); + CmsAuthenticator authenticator = isPublic ? new PublicCmsAuthenticator() : new CmsAuthenticator(); httpHandlers.put(contextPath, httpHandler); + httpAuthenticators.put(contextPath, authenticator); if (httpServer == null) return; - httpServer.createContext(contextPath, httpHandler); - log.debug(() -> "Added handler " + contextPath + " : " + httpHandler.getClass().getName()); + else + createHttpContext(contextPath, httpHandler, authenticator); + } + public void createHttpContext(String contextPath, HttpHandler httpHandler, CmsAuthenticator authenticator) { + HttpContext httpContext = httpServer.createContext(contextPath); + // we want to set the authenticator BEFORE the handler actually becomes active + httpContext.setAuthenticator(authenticator); + httpContext.setHandler(httpHandler); + log.debug(() -> "Added handler " + contextPath + " : " + httpHandler.getClass().getName()); } public void removeHttpHandler(HttpHandler httpHandler, Map properties) { @@ -115,8 +98,17 @@ public class CmsDeploymentImpl implements CmsDeployment { httpServer.removeContext(contextPath); log.debug(() -> "Removed handler " + contextPath + " : " + httpHandler.getClass().getName()); } -// public void setHttpService(HttpService httpService) { -// this.httpService = httpService; -// } + + public boolean allExpectedServicesAvailable() { + if (httpExpected && httpServer == null) + return false; + if (sshdExpected && cmsSshd == null) + return false; + return true; + } + + public void setCmsSshd(CmsSshd cmsSshd) { + this.cmsSshd = cmsSshd; + } } -- 2.30.2