Improve CMS start up and status reporting.
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 21 Jul 2022 07:02:53 +0000 (09:02 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 21 Jul 2022 07:02:53 +0000 (09:02 +0200)
24 files changed:
org.argeo.api.cms/src/org/argeo/api/cms/CmsConstants.java
org.argeo.cms.ee/OSGI-INF/statusHandler.xml
org.argeo.cms.ee/src/org/argeo/cms/websocket/server/CmsWebSocketConfigurator.java
org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PingEndpoint.java [new file with mode: 0644]
org.argeo.cms.ee/src/org/argeo/cms/websocket/server/PublicWebSocketConfigurator.java [new file with mode: 0644]
org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusEndpoints.java [deleted file]
org.argeo.cms.ee/src/org/argeo/cms/websocket/server/StatusHandler.java [new file with mode: 0644]
org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml
org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/CmsJettyServer.java
org.argeo.cms.lib.sshd/OSGI-INF/cmsSshServer.xml
org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/CmsSshServer.java
org.argeo.cms/OSGI-INF/cmsDeployment.xml
org.argeo.cms/bnd.bnd
org.argeo.cms/src/org/argeo/cms/CmsSshd.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java
org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java
org.argeo.cms/src/org/argeo/cms/client/WebSocketEventClient.java
org.argeo.cms/src/org/argeo/cms/client/WsPing.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/auth/RemoteCmsSessionImpl.java
org.argeo.cms/src/org/argeo/cms/internal/http/AbstractHttpAuthenticator.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/http/CmsAuthenticator.java
org.argeo.cms/src/org/argeo/cms/internal/http/PublicCmsAuthenticator.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java

index 98364d97b9781220cb3f1c4e2725dd4ed1f143a2..34d85a252cd8eb09c8e8875a34260257b15c0f6b 100644 (file)
@@ -83,6 +83,7 @@ public interface CmsConstants {
         * COMPONENT PROPERTIES
         */
        String CONTEXT_PATH = "context.path";
+       String CONTEXT_PUBLIC = "context.public";
        String EVENT_TOPICS = "event.topics";
 
        /*
index a530c336a29a2966c447972cac4d277c3deb7978..8461cff1fb29edb07e4b8b9016ddc949f8b2e24a 100644 (file)
@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immmediate="true" name="Status Handler">
-   <implementation class="org.argeo.cms.websocket.server.StatusEndpoints"/>
+   <implementation class="org.argeo.cms.websocket.server.StatusHandler"/>
    <service>
       <provide interface="com.sun.net.httpserver.HttpHandler"/>
    </service>
    <property name="context.path" type="String" value="/cms/status"/>
+   <reference bind="setCmsState" cardinality="1..1" interface="org.argeo.api.cms.CmsState" name="CmsState" policy="static"/>
 </scr:component>
index 279b610b6edf8cb9d0ef57943cde4ae73762eafb..0ded176f46ffef6a291a13cedc342cc01653a80e 100644 (file)
@@ -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 (file)
index 0000000..b81cc59
--- /dev/null
@@ -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 (file)
index 0000000..e3e17cf
--- /dev/null
@@ -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 (file)
index d5839e2..0000000
+++ /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<Class<?>> getEndPoints() {
-               Set<Class<?>> 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 (file)
index 0000000..a8466fe
--- /dev/null
@@ -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<Class<?>> getEndPoints() {
+               Set<Class<?>> 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<String> 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;
+       }
+
+}
index e2c23f85df97a163b05af8fe8a1b0cdb9710857b..6a13362209004ee161daf08978d28d33308b59a1 100644 (file)
@@ -3,7 +3,6 @@
    <implementation class="org.argeo.cms.equinox.http.jetty.EquinoxJettyServer"/>
    <property name="service.pid" type="String" value="org.argeo.equinox.jetty.config"/>
    <reference bind="setCmsState" cardinality="1..1" interface="org.argeo.api.cms.CmsState" name="CmsState" policy="static"/>
-   <reference bind="setDefaultAuthenticator" cardinality="1..1" interface="com.sun.net.httpserver.Authenticator" policy="static"/>
    <service>
       <provide interface="com.sun.net.httpserver.HttpServer"/>
    </service>
index a18f4b495632f908c9be762ac1587c49af46b5b2..4927e6f1006ab5da0d7b9eac079bbc31052c7a73 100644 (file)
@@ -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;
+//     }
 
 }
index aa2d8db2d8c22e84bd52fc58bd6cd1866b0ccfbd..987b977459aef7473db12180e7afa7f78628eb25 100644 (file)
@@ -2,4 +2,7 @@
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" name="CMS SSH Server" immediate="true">
    <implementation class="org.argeo.cms.ssh.CmsSshServer"/>
    <reference bind="setCmsState" cardinality="1..1" interface="org.argeo.api.cms.CmsState" name="CmsState" policy="static"/>
+   <service>
+      <provide interface="org.argeo.cms.CmsSshd"/>
+   </service>
 </scr:component>
index 7246d603db49feeda99a8dad729da72208c39805..ab62654f23a32f521171904b42a394c278070317 100644 (file)
@@ -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();
index b00bc82f34be609199432beff19fc254ffea83fb..ea54fc2f9c15e1318a8820ed3d407ebe3f49bfd7 100644 (file)
@@ -2,7 +2,8 @@
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" immediate="true" name="CMS Deployment">
    <implementation class="org.argeo.cms.internal.runtime.CmsDeploymentImpl"/>
    <reference bind="setCmsState" cardinality="1..1" interface="org.argeo.api.cms.CmsState" name="CmsState" policy="static"/>
-   <reference bind="setHttpServer" cardinality="0..1" interface="com.sun.net.httpserver.HttpServer" policy="static"/>
+   <reference bind="setCmsSshd" cardinality="0..1" interface="org.argeo.cms.CmsSshd" policy="dynamic"/>
+   <reference bind="setHttpServer" cardinality="0..1" interface="com.sun.net.httpserver.HttpServer" policy="dynamic"/>
    <reference bind="addHttpHandler" unbind="removeHttpHandler" cardinality="0..1" interface="com.sun.net.httpserver.HttpHandler" policy="dynamic"/>
    <service>
       <provide interface="org.argeo.api.cms.CmsDeployment"/>
index bddc21f27467e144475a0dfdf223d3c8da7d0ccb..528afc812b4ab96847a9a62e9de7ecbbfb9b9bd7 100644 (file)
@@ -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 (file)
index 0000000..e38bf63
--- /dev/null
@@ -0,0 +1,6 @@
+package org.argeo.cms;
+
+/** Just a marker interface for the time being.*/
+public interface CmsSshd {
+
+}
index 6abaf71f262ac676d33efb5eff0968039c9378c1..396af9343f48d74a3933c503b7437d6003c959ba 100644 (file)
@@ -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);
index e6c425f692f6455069953968fe99e57144e24014..1f2ea73a75cebb04a3eb65ef0cbed5e7a3dd997e 100644 (file)
@@ -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();
index 0787b04780c4d00890869514f263d9dbbaf52c32..5834bd611da137cb4192cd76b8d7572f7eb7b8b6 100644 (file)
@@ -50,6 +50,7 @@ public class WebSocketEventClient {
                CompletableFuture<WebSocket> 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 (file)
index 0000000..0470b9f
--- /dev/null
@@ -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<WebSocket> 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() + " <url>");
+                       System.exit(1);
+                       return;
+               }
+               URI uri = URI.create(args[0]);
+               new WsPing(uri).run();
+       }
+
+}
index 41ee7797b6c4f7cab63f81ec72acd0d5263a163f..23f5d8427e061001b83e3b546a14ab02dc652ce2 100644 (file)
@@ -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 (file)
index 418b068..0000000
+++ /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);
-
-
-}
index 61d2d306604e950ea83e66f207d100216584bec2..307f928a5c9658f52bca202cf8d24f0655c0e403 100644 (file)
@@ -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 (file)
index 0000000..14d79a6
--- /dev/null
@@ -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;
+       }
+
+}
index 7a0f3388c07a4f79cabdd6928de9fa210e3bc511..940e329a358fd8deab1a717d5471772d1d042eb2 100644 (file)
@@ -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");
                }
        }
 
index 497ed923fa5f9ee2ae9bd46890f27ce39eae7754..67daef2c8c8e5ce249b7cb8107b4626bf55f2e2b 100644 (file)
@@ -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<String, HttpHandler> httpHandlers = new TreeMap<>();
+       private Map<String, CmsAuthenticator> 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<String, Object> props) {
-//             deployConfig.putFactoryDeployConfig(factoryPid, props);
-//             deployConfig.save();
-//             try {
-//                     deployConfig.loadConfigs();
-//             } catch (IOException e) {
-//                     throw new IllegalStateException(e);
-//             }
-//     }
-//
-//     public Dictionary<String, Object> 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<String, String> 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;
+       }
 
 }