From 336930c69f0cd3e1242e518479624c6366541275 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Mon, 27 Jun 2022 17:50:11 +0200 Subject: [PATCH] Disable OSGi configuration admin and LDIF-based deploy config. --- .../OSGI-INF/jettyServiceFactory.xml | 8 +- .../argeo/cms/servlet/CmsServletContext.java | 4 + .../jetty}/InternalHttpConstants.java | 2 +- .../servlet/internal/jetty/JettyConfig.java | 231 ++++++++++++++++++ .../OSGI-INF/jcrDeployment.xml | 1 - .../OSGI-INF/repositoryContextsFactory.xml | 5 +- .../argeo/cms/internal/jcr/JcrInitUtils.java | 77 +++--- .../cms/jcr/internal/CmsJcrDeployment.java | 13 +- .../internal/RepositoryContextsFactory.java | 138 +++++++---- .../src/org/argeo/api/cms/CmsDeployment.java | 6 +- .../src/org/argeo/api/cms/CmsState.java | 5 + org.argeo.cms/OSGI-INF/cmsDeployment.xml | 1 - org.argeo.cms/OSGI-INF/nodeUserAdmin.xml | 10 +- org.argeo.cms/bnd.bnd | 1 - .../cms/auth/RemoteSessionLoginModule.java | 3 +- .../argeo/cms/internal/osgi/CmsActivator.java | 27 +- .../argeo/cms/internal/osgi/DeployConfig.java | 79 +++--- .../internal/runtime/CmsDeploymentImpl.java | 48 ++-- .../cms/internal/runtime/CmsStateImpl.java | 11 + .../cms/internal/runtime/CmsUserAdmin.java | 33 ++- .../argeo/cms/internal/runtime/InitUtils.java | 212 +++++++--------- .../cms/internal/runtime/KernelConstants.java | 3 - .../cms/internal/runtime/KernelUtils.java | 2 +- .../src/org/argeo/cms/runtime/StaticCms.java | 22 +- .../runtime => security}/PkiUtils.java | 47 +++- .../osgi/useradmin/AggregatingUserAdmin.java | 16 +- 26 files changed, 655 insertions(+), 350 deletions(-) rename {org.argeo.cms/src/org/argeo/cms/internal/http => eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/internal/jetty}/InternalHttpConstants.java (95%) create mode 100644 eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java rename org.argeo.cms/src/org/argeo/cms/{internal/runtime => security}/PkiUtils.java (84%) diff --git a/eclipse/org.argeo.cms.servlet/OSGI-INF/jettyServiceFactory.xml b/eclipse/org.argeo.cms.servlet/OSGI-INF/jettyServiceFactory.xml index c007351aa..c2cf1c73b 100644 --- a/eclipse/org.argeo.cms.servlet/OSGI-INF/jettyServiceFactory.xml +++ b/eclipse/org.argeo.cms.servlet/OSGI-INF/jettyServiceFactory.xml @@ -1,8 +1,6 @@ - - - - - + + + diff --git a/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/CmsServletContext.java b/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/CmsServletContext.java index 1ae6286ac..cc2bc02d1 100644 --- a/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/CmsServletContext.java +++ b/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/CmsServletContext.java @@ -76,7 +76,9 @@ public class CmsServletContext extends ServletContextHelper { protected LoginContext processUnauthorized(HttpServletRequest request, HttpServletResponse response) { // anonymous + ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader(); try { + Thread.currentThread().setContextClassLoader(CmsServletContext.class.getClassLoader()); LoginContext lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS, new RemoteAuthCallbackHandler(new ServletHttpRequest(request), new ServletHttpResponse(response))); lc.login(); @@ -85,6 +87,8 @@ public class CmsServletContext extends ServletContextHelper { if (log.isDebugEnabled()) log.error("Cannot log in as anonymous", e1); return null; + } finally { + Thread.currentThread().setContextClassLoader(currentContextClassLoader); } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/InternalHttpConstants.java b/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/internal/jetty/InternalHttpConstants.java similarity index 95% rename from org.argeo.cms/src/org/argeo/cms/internal/http/InternalHttpConstants.java rename to eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/internal/jetty/InternalHttpConstants.java index c888c291b..462bf7a6b 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/InternalHttpConstants.java +++ b/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/internal/jetty/InternalHttpConstants.java @@ -1,4 +1,4 @@ -package org.argeo.cms.internal.http; +package org.argeo.cms.servlet.internal.jetty; /** Compatible with Jetty. */ public interface InternalHttpConstants { diff --git a/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java b/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java new file mode 100644 index 000000000..026993eab --- /dev/null +++ b/eclipse/org.argeo.cms.servlet/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java @@ -0,0 +1,231 @@ +package org.argeo.cms.servlet.internal.jetty; + +import java.io.IOException; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyStore; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.Map; +import java.util.concurrent.ForkJoinPool; + +import javax.websocket.DeploymentException; +import javax.websocket.server.ServerContainer; +import javax.websocket.server.ServerEndpointConfig; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.CmsState; +import org.argeo.cms.security.PkiUtils; +import org.argeo.cms.websocket.javax.server.CmsWebSocketConfigurator; +import org.argeo.cms.websocket.javax.server.TestEndpoint; +import org.argeo.util.LangUtils; +import org.eclipse.equinox.http.jetty.JettyConfigurator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +public class JettyConfig { + private final static CmsLog log = CmsLog.getLog(JettyConfig.class); + + final static String CMS_JETTY_CUSTOMIZER_CLASS = "org.argeo.equinox.jetty.CmsJettyCustomizer"; + // Argeo specific + final static String WEBSOCKET_ENABLED = "websocket.enabled"; + + private CmsState cmsState; + + private final BundleContext bc = FrameworkUtil.getBundle(JettyConfig.class).getBundleContext(); + + public void start() { + // We need to start asynchronously so that Jetty bundle get started by lazy init + // due to the non-configurable behaviour of its activator + ForkJoinPool.commonPool().execute(() -> { + Dictionary properties = getHttpServerConfig(); + startServer(properties); + }); + + ServiceTracker serverSt = new ServiceTracker( + bc, ServerContainer.class, null) { + + @Override + public ServerContainer addingService(ServiceReference reference) { + ServerContainer serverContainer = super.addingService(reference); + + BundleContext bc = reference.getBundle().getBundleContext(); + ServiceReference srConfigurator = bc + .getServiceReference(ServerEndpointConfig.Configurator.class); + ServerEndpointConfig.Configurator endpointConfigurator = bc.getService(srConfigurator); + ServerEndpointConfig config = ServerEndpointConfig.Builder + .create(TestEndpoint.class, "/ws/test/events/").configurator(endpointConfigurator).build(); + try { + serverContainer.addEndpoint(config); + } catch (DeploymentException e) { + throw new IllegalStateException("Cannot initalise the WebSocket server runtime.", e); + } + return serverContainer; + } + + }; + serverSt.open(); + } + + public void stop() { + try { + JettyConfigurator.stopServer(CmsConstants.DEFAULT); + } catch (Exception e) { + log.error("Cannot stop default Jetty server.", e); + } + + } + + public void startServer(Dictionary properties) { + // Explicitly configures Jetty so that the default server is not started by the + // activator of the Equinox Jetty bundle. + Map config = LangUtils.dictToStringMap(properties); + if (!config.isEmpty()) { + config.put("customizer.class", CMS_JETTY_CUSTOMIZER_CLASS); + + // TODO centralise with Jetty extender + Object webSocketEnabled = config.get(WEBSOCKET_ENABLED); + if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) { + bc.registerService(ServerEndpointConfig.Configurator.class, new CmsWebSocketConfigurator(), null); + config.put(WEBSOCKET_ENABLED, "true"); + } + } + + int tryCount = 30; + try { + tryGettyJetty: while (tryCount > 0) { + try { + // FIXME deal with multiple ids + JettyConfigurator.startServer(CmsConstants.DEFAULT, new Hashtable<>(config)); + + Object httpPort = config.get(InternalHttpConstants.HTTP_PORT); + Object httpsPort = config.get(InternalHttpConstants.HTTPS_PORT); + log.info(httpPortsMsg(httpPort, httpsPort)); + + // Explicitly starts Jetty OSGi HTTP bundle, so that it gets triggered if OSGi + // configuration is not cleaned + FrameworkUtil.getBundle(JettyConfigurator.class).start(); + break tryGettyJetty; + } catch (IllegalStateException e) { + // e.printStackTrace(); + // Jetty may not be ready + try { + Thread.sleep(1000); + } catch (Exception e1) { + // silent + } + tryCount--; + } + } + } catch (Exception e) { + log.error("Cannot start default Jetty server with config " + properties, e); + } + + } + + private String httpPortsMsg(Object httpPort, Object httpsPort) { + return (httpPort != null ? "HTTP " + httpPort + " " : " ") + (httpsPort != null ? "HTTPS " + httpsPort : ""); + } + + /** Override the provided config with the framework properties */ + public Dictionary getHttpServerConfig() { + String httpPort = getFrameworkProp("org.osgi.service.http.port"); + String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure"); + /// TODO make it more generic + String httpHost = getFrameworkProp( + InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTP_HOST); + String httpsHost = getFrameworkProp( + InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTPS_HOST); + String webSocketEnabled = getFrameworkProp( + InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.WEBSOCKET_ENABLED); + + final Hashtable props = new Hashtable(); + // try { + if (httpPort != null || httpsPort != null) { + boolean httpEnabled = httpPort != null; + props.put(InternalHttpConstants.HTTP_ENABLED, httpEnabled); + boolean httpsEnabled = httpsPort != null; + props.put(InternalHttpConstants.HTTPS_ENABLED, httpsEnabled); + + if (httpEnabled) { + props.put(InternalHttpConstants.HTTP_PORT, httpPort); + if (httpHost != null) + props.put(InternalHttpConstants.HTTP_HOST, httpHost); + } + + if (httpsEnabled) { + props.put(InternalHttpConstants.HTTPS_PORT, httpsPort); + if (httpsHost != null) + props.put(InternalHttpConstants.HTTPS_HOST, httpsHost); + + // server certificate + Path keyStorePath = cmsState.getDataPath(PkiUtils.DEFAULT_KEYSTORE_PATH); + Path pemKeyPath = cmsState.getDataPath(PkiUtils.DEFAULT_PEM_KEY_PATH); + Path pemCertPath = cmsState.getDataPath(PkiUtils.DEFAULT_PEM_CERT_PATH); + String keyStorePasswordStr = getFrameworkProp( + InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_PASSWORD); + char[] keyStorePassword; + if (keyStorePasswordStr == null) + keyStorePassword = "changeit".toCharArray(); + else + keyStorePassword = keyStorePasswordStr.toCharArray(); + + // if PEM files both exists, update the PKCS12 file + if (Files.exists(pemCertPath) && Files.exists(pemKeyPath)) { + // TODO check certificate update time? monitor changes? + KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12); + try (Reader key = Files.newBufferedReader(pemKeyPath, StandardCharsets.US_ASCII); + Reader cert = Files.newBufferedReader(pemCertPath, StandardCharsets.US_ASCII);) { + PkiUtils.loadPem(keyStore, key, keyStorePassword, cert); + PkiUtils.saveKeyStore(keyStorePath, keyStorePassword, keyStore); + if (log.isDebugEnabled()) + log.debug("PEM certificate stored in " + keyStorePath); + } catch (IOException e) { + log.error("Cannot read PEM files " + pemKeyPath + " and " + pemCertPath, e); + } + } + + if (!Files.exists(keyStorePath)) + PkiUtils.createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12); + props.put(InternalHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12); + props.put(InternalHttpConstants.SSL_KEYSTORE, keyStorePath.toString()); + props.put(InternalHttpConstants.SSL_PASSWORD, new String(keyStorePassword)); + +// props.put(InternalHttpConstants.SSL_KEYSTORETYPE, "PKCS11"); +// props.put(InternalHttpConstants.SSL_KEYSTORE, "../../nssdb"); +// props.put(InternalHttpConstants.SSL_PASSWORD, keyStorePassword); + + // client certificate authentication + String wantClientAuth = getFrameworkProp( + InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_WANTCLIENTAUTH); + if (wantClientAuth != null) + props.put(InternalHttpConstants.SSL_WANTCLIENTAUTH, Boolean.parseBoolean(wantClientAuth)); + String needClientAuth = getFrameworkProp( + InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_NEEDCLIENTAUTH); + if (needClientAuth != null) + props.put(InternalHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth)); + } + + // web socket + if (webSocketEnabled != null && webSocketEnabled.equals("true")) + props.put(InternalHttpConstants.WEBSOCKET_ENABLED, true); + + props.put(CmsConstants.CN, CmsConstants.DEFAULT); + } + return props; + } + + private String getFrameworkProp(String key) { + return cmsState.getDeployProperty(key); + } + + public void setCmsState(CmsState cmsState) { + this.cmsState = cmsState; + } + +} diff --git a/jcr/org.argeo.cms.jcr/OSGI-INF/jcrDeployment.xml b/jcr/org.argeo.cms.jcr/OSGI-INF/jcrDeployment.xml index a94b15168..033ddbd2f 100644 --- a/jcr/org.argeo.cms.jcr/OSGI-INF/jcrDeployment.xml +++ b/jcr/org.argeo.cms.jcr/OSGI-INF/jcrDeployment.xml @@ -1,5 +1,4 @@ - diff --git a/jcr/org.argeo.cms.jcr/OSGI-INF/repositoryContextsFactory.xml b/jcr/org.argeo.cms.jcr/OSGI-INF/repositoryContextsFactory.xml index db2bfaa26..2d06acad5 100644 --- a/jcr/org.argeo.cms.jcr/OSGI-INF/repositoryContextsFactory.xml +++ b/jcr/org.argeo.cms.jcr/OSGI-INF/repositoryContextsFactory.xml @@ -2,8 +2,5 @@ - - - - + diff --git a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/internal/jcr/JcrInitUtils.java b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/internal/jcr/JcrInitUtils.java index 0536fb645..a1cba1a62 100644 --- a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/internal/jcr/JcrInitUtils.java +++ b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/internal/jcr/JcrInitUtils.java @@ -22,30 +22,31 @@ import org.osgi.framework.Constants; import org.osgi.framework.FrameworkUtil; /** JCR specific init utilities. */ +@Deprecated public class JcrInitUtils { private final static CmsLog log = CmsLog.getLog(JcrInitUtils.class); private final static BundleContext bundleContext = FrameworkUtil.getBundle(JcrInitUtils.class).getBundleContext(); - public static void addToDeployment(CmsDeployment nodeDeployment) { - // node repository -// Dictionary provided = null; - Dictionary provided = nodeDeployment.getProps(CmsConstants.NODE_REPOS_FACTORY_PID, - CmsConstants.NODE); - Dictionary nodeConfig = JcrInitUtils.getNodeRepositoryConfig(provided); - // node repository is mandatory - nodeDeployment.addFactoryDeployConfig(CmsConstants.NODE_REPOS_FACTORY_PID, nodeConfig); - - // additional repositories -// dataModels: for (DataModels.DataModel dataModel : dataModels.getNonAbstractDataModels()) { -// if (NodeConstants.NODE_REPOSITORY.equals(dataModel.getName())) -// continue dataModels; -// Dictionary config = JcrInitUtils.getRepositoryConfig(dataModel.getName(), -// getProps(NodeConstants.NODE_REPOS_FACTORY_PID, dataModel.getName())); -// if (config.size() != 0) -// putFactoryDeployConfig(NodeConstants.NODE_REPOS_FACTORY_PID, config); -// } - - } +// public static void addToDeployment(CmsDeployment nodeDeployment) { +// // node repository +//// Dictionary provided = null; +// Dictionary provided = nodeDeployment.getProps(CmsConstants.NODE_REPOS_FACTORY_PID, +// CmsConstants.NODE); +// Dictionary nodeConfig = JcrInitUtils.getNodeRepositoryConfig(provided); +// // node repository is mandatory +// nodeDeployment.addFactoryDeployConfig(CmsConstants.NODE_REPOS_FACTORY_PID, nodeConfig); +// +// // additional repositories +//// dataModels: for (DataModels.DataModel dataModel : dataModels.getNonAbstractDataModels()) { +//// if (NodeConstants.NODE_REPOSITORY.equals(dataModel.getName())) +//// continue dataModels; +//// Dictionary config = JcrInitUtils.getRepositoryConfig(dataModel.getName(), +//// getProps(NodeConstants.NODE_REPOS_FACTORY_PID, dataModel.getName())); +//// if (config.size() != 0) +//// putFactoryDeployConfig(NodeConstants.NODE_REPOS_FACTORY_PID, config); +//// } +// +// } /** Override the provided config with the framework properties */ public static Dictionary getNodeRepositoryConfig(Dictionary provided) { @@ -62,24 +63,24 @@ public class JcrInitUtils { return props; } - public static Dictionary getRepositoryConfig(String dataModelName, - Dictionary provided) { - if (dataModelName.equals(CmsConstants.NODE_REPOSITORY) || dataModelName.equals(CmsConstants.EGO_REPOSITORY)) - throw new IllegalArgumentException("Data model '" + dataModelName + "' is reserved."); - Dictionary props = provided != null ? provided : new Hashtable(); - for (RepoConf repoConf : RepoConf.values()) { - Object value = getFrameworkProp( - CmsConstants.NODE_REPOS_PROP_PREFIX + dataModelName + '.' + repoConf.name()); - if (value != null) { - props.put(repoConf.name(), value); - if (log.isDebugEnabled()) - log.debug("Set " + dataModelName + " repo configuration " + repoConf.name() + " to " + value); - } - } - if (props.size() != 0) - props.put(CmsConstants.CN, dataModelName); - return props; - } +// public static Dictionary getRepositoryConfig(String dataModelName, +// Dictionary provided) { +// if (dataModelName.equals(CmsConstants.NODE_REPOSITORY) || dataModelName.equals(CmsConstants.EGO_REPOSITORY)) +// throw new IllegalArgumentException("Data model '" + dataModelName + "' is reserved."); +// Dictionary props = provided != null ? provided : new Hashtable(); +// for (RepoConf repoConf : RepoConf.values()) { +// Object value = getFrameworkProp( +// CmsConstants.NODE_REPOS_PROP_PREFIX + dataModelName + '.' + repoConf.name()); +// if (value != null) { +// props.put(repoConf.name(), value); +// if (log.isDebugEnabled()) +// log.debug("Set " + dataModelName + " repo configuration " + repoConf.name() + " to " + value); +// } +// } +// if (props.size() != 0) +// props.put(CmsConstants.CN, dataModelName); +// return props; +// } private static void registerRemoteInit(String uri) { try { diff --git a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/CmsJcrDeployment.java b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/CmsJcrDeployment.java index 8fb61d70c..6fdd2009b 100644 --- a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/CmsJcrDeployment.java +++ b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/CmsJcrDeployment.java @@ -75,7 +75,7 @@ public class CmsJcrDeployment { // Readiness private boolean nodeAvailable = false; - CmsDeployment cmsDeployment; +// CmsDeployment cmsDeployment; public void start() { dataModels = new DataModels(bc); @@ -85,7 +85,7 @@ public class CmsJcrDeployment { // nodeDeployment = CmsJcrActivator.getService(NodeDeployment.class); - JcrInitUtils.addToDeployment(cmsDeployment); + //JcrInitUtils.addToDeployment(cmsDeployment); // contentRepository.registerTypes(NamespaceRegistry.PREFIX_JCR, NamespaceRegistry.NAMESPACE_JCR, null); // contentRepository.registerTypes(NamespaceRegistry.PREFIX_MIX, NamespaceRegistry.NAMESPACE_MIX, null); @@ -113,9 +113,9 @@ public class CmsJcrDeployment { } - public void setCmsDeployment(CmsDeployment cmsDeployment) { - this.cmsDeployment = cmsDeployment; - } +// public void setCmsDeployment(CmsDeployment cmsDeployment) { +// this.cmsDeployment = cmsDeployment; +// } /** * Checks whether the deployment is available according to expectations, and @@ -363,7 +363,8 @@ public class CmsJcrDeployment { } boolean isStandalone(String dataModelName) { - return cmsDeployment.getProps(CmsConstants.NODE_REPOS_FACTORY_PID, dataModelName) != null; + return true; + //return cmsDeployment.getProps(CmsConstants.NODE_REPOS_FACTORY_PID, dataModelName) != null; } private void publishLocalRepo(String dataModelName, Repository repository) { diff --git a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/RepositoryContextsFactory.java b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/RepositoryContextsFactory.java index e05a0023e..11e9a9e92 100644 --- a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/RepositoryContextsFactory.java +++ b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/RepositoryContextsFactory.java @@ -5,6 +5,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Dictionary; import java.util.HashMap; +import java.util.Hashtable; import java.util.Map; import javax.jcr.Repository; @@ -14,86 +15,111 @@ import javax.jcr.RepositoryFactory; import org.apache.jackrabbit.core.RepositoryContext; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.CmsState; import org.argeo.cms.internal.jcr.RepoConf; import org.argeo.cms.internal.jcr.RepositoryBuilder; import org.argeo.cms.jcr.internal.osgi.CmsJcrActivator; import org.argeo.util.LangUtils; -import org.osgi.framework.Constants; -import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedServiceFactory; /** A {@link ManagedServiceFactory} creating or referencing JCR repositories. */ -public class RepositoryContextsFactory implements ManagedServiceFactory { +public class RepositoryContextsFactory { private final static CmsLog log = CmsLog.getLog(RepositoryContextsFactory.class); // private final BundleContext bc = FrameworkUtil.getBundle(RepositoryServiceFactory.class).getBundleContext(); - private Map repositories = new HashMap(); - private Map pidToCn = new HashMap(); +// private Map repositories = new HashMap(); +// private Map pidToCn = new HashMap(); - public void init() { + private RepositoryContext repositoryContext; + + private CmsState cmsState; + public void init() { + Dictionary config = getNodeRepositoryConfig(); + deployRepository(config); } public void destroy() { - for (String pid : repositories.keySet()) { - try { - RepositoryContext repositoryContext = repositories.get(pid); - // Must start in another thread otherwise shutdown is interrupted - // TODO use an executor? - new Thread(() -> { - repositoryContext.getRepository().shutdown(); - if (log.isDebugEnabled()) - log.debug("Shut down repository " + pid - + (pidToCn.containsKey(pid) ? " (" + pidToCn.get(pid) + ")" : "")); - }, "Shutdown JCR repository " + pid).start(); - } catch (Exception e) { - log.error("Error when shutting down Jackrabbit repository " + pid, e); - } + if (this.repositoryContext != null) { + this.repositoryContext.getRepository().shutdown(); } +// for (String pid : repositories.keySet()) { +// try { +// RepositoryContext repositoryContext = repositories.get(pid); +// // Must start in another thread otherwise shutdown is interrupted +// // TODO use an executor? +// new Thread(() -> { +// repositoryContext.getRepository().shutdown(); +// if (log.isDebugEnabled()) +// log.debug("Shut down repository " + pid +// + (pidToCn.containsKey(pid) ? " (" + pidToCn.get(pid) + ")" : "")); +// }, "Shutdown JCR repository " + pid).start(); +// } catch (Exception e) { +// log.error("Error when shutting down Jackrabbit repository " + pid, e); +// } +// } } - @Override - public String getName() { - return "Jackrabbit repository service factory"; +// @Override +// public String getName() { +// return "Jackrabbit repository service factory"; +// } + + /** Override the provided config with the framework properties */ + private Dictionary getNodeRepositoryConfig() { + Dictionary props = new Hashtable(); + for (RepoConf repoConf : RepoConf.values()) { + Object value = getFrameworkProp(CmsConstants.NODE_REPO_PROP_PREFIX + repoConf.name()); + if (value != null) { + props.put(repoConf.name(), value); + if (log.isDebugEnabled()) + log.debug("Set node repo configuration " + repoConf.name() + " to " + value); + } + } + props.put(CmsConstants.CN, CmsConstants.NODE_REPOSITORY); + return props; } - @Override - public void updated(String pid, Dictionary properties) throws ConfigurationException { - if (repositories.containsKey(pid)) - throw new IllegalArgumentException("Already a repository registered for " + pid); +// @Override +// public void updated(String pid, Dictionary properties) throws ConfigurationException { + protected void deployRepository(Dictionary properties) { +// if (repositories.containsKey(pid)) +// throw new IllegalArgumentException("Already a repository registered for " + pid); if (properties == null) return; Object cn = properties.get(CmsConstants.CN); - if (cn != null) - for (String otherPid : pidToCn.keySet()) { - Object o = pidToCn.get(otherPid); - if (cn.equals(o)) { - RepositoryContext repositoryContext = repositories.remove(otherPid); - repositories.put(pid, repositoryContext); - if (log.isDebugEnabled()) - log.debug("Ignoring update of Jackrabbit repository " + cn); - // FIXME perform a proper update (also of the OSGi service) - return; - } - } +// if (cn != null) +// for (String otherPid : pidToCn.keySet()) { +// Object o = pidToCn.get(otherPid); +// if (cn.equals(o)) { +// RepositoryContext repositoryContext = repositories.remove(otherPid); +// repositories.put(pid, repositoryContext); +// if (log.isDebugEnabled()) +// log.debug("Ignoring update of Jackrabbit repository " + cn); +// // FIXME perform a proper update (also of the OSGi service) +// return; +// } +// } try { Object labeledUri = properties.get(RepoConf.labeledUri.name()); if (labeledUri == null) { RepositoryBuilder repositoryBuilder = new RepositoryBuilder(); RepositoryContext repositoryContext = repositoryBuilder.createRepositoryContext(properties); - repositories.put(pid, repositoryContext); - Dictionary props = LangUtils.dict(Constants.SERVICE_PID, pid); +// repositories.put(pid, repositoryContext); +// Dictionary props = LangUtils.dict(Constants.SERVICE_PID, pid); + Dictionary props = new Hashtable<>(); // props.put(ArgeoJcrConstants.JCR_REPOSITORY_URI, // properties.get(RepoConf.labeledUri.name())); if (cn != null) { props.put(CmsConstants.CN, cn); // props.put(NodeConstants.JCR_REPOSITORY_ALIAS, cn); - pidToCn.put(pid, cn); +// pidToCn.put(pid, cn); } CmsJcrActivator.registerService(RepositoryContext.class, repositoryContext, props); + this.repositoryContext = repositoryContext; } else { Object defaultWorkspace = properties.get(RepoConf.defaultWorkspace.name()); if (defaultWorkspace == null) @@ -108,14 +134,14 @@ public class RepositoryContextsFactory implements ManagedServiceFactory { Repository repository = repositoryFactory.getRepository(parameters); // Repository repository = NodeUtils.getRepositoryByUri(repositoryFactory, // uri.toString()); - Dictionary props = LangUtils.dict(Constants.SERVICE_PID, pid); +// Dictionary props = LangUtils.dict(Constants.SERVICE_PID, pid); + Dictionary props = new Hashtable<>(); props.put(RepoConf.labeledUri.name(), new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null) .toString()); if (cn != null) { props.put(CmsConstants.CN, cn); - // props.put(NodeConstants.JCR_REPOSITORY_ALIAS, cn); - pidToCn.put(pid, cn); +// pidToCn.put(pid, cn); } CmsJcrActivator.registerService(Repository.class, repository, props); @@ -127,17 +153,25 @@ public class RepositoryContextsFactory implements ManagedServiceFactory { } } } catch (RepositoryException | URISyntaxException | IOException e) { - throw new IllegalStateException("Cannot create Jackrabbit repository " + pid, e); + throw new IllegalStateException("Cannot create Jackrabbit repository " + properties, e); } } - @Override - public void deleted(String pid) { - RepositoryContext repositoryContext = repositories.remove(pid); - repositoryContext.getRepository().shutdown(); - if (log.isDebugEnabled()) - log.debug("Deleted repository " + pid); +// @Override +// public void deleted(String pid) { +// RepositoryContext repositoryContext = repositories.remove(pid); +// repositoryContext.getRepository().shutdown(); +// if (log.isDebugEnabled()) +// log.debug("Deleted repository " + pid); +// } + + private String getFrameworkProp(String key) { + return cmsState.getDeployProperty(key); + } + + public void setCmsState(CmsState cmsState) { + this.cmsState = cmsState; } } diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/CmsDeployment.java b/org.argeo.api.cms/src/org/argeo/api/cms/CmsDeployment.java index 5893d2ec5..c69be8eeb 100644 --- a/org.argeo.api.cms/src/org/argeo/api/cms/CmsDeployment.java +++ b/org.argeo.api.cms/src/org/argeo/api/cms/CmsDeployment.java @@ -5,7 +5,7 @@ import java.util.Dictionary; /** A configured node deployment. */ public interface CmsDeployment { - void addFactoryDeployConfig(String factoryPid, Dictionary props); - - Dictionary getProps(String factoryPid, String cn); +// void addFactoryDeployConfig(String factoryPid, Dictionary props); +// +// Dictionary getProps(String factoryPid, String cn); } 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 3828b080b..3e0e8b757 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 @@ -1,5 +1,6 @@ package org.argeo.api.cms; +import java.nio.file.Path; import java.util.UUID; /** A running node process. */ @@ -9,4 +10,8 @@ public interface CmsState { Long getAvailableSince(); UUID getUuid(); + + String getDeployProperty(String key); + + Path getDataPath(String relativePath); } diff --git a/org.argeo.cms/OSGI-INF/cmsDeployment.xml b/org.argeo.cms/OSGI-INF/cmsDeployment.xml index d36a91110..5f5ed5fcc 100644 --- a/org.argeo.cms/OSGI-INF/cmsDeployment.xml +++ b/org.argeo.cms/OSGI-INF/cmsDeployment.xml @@ -1,6 +1,5 @@ - diff --git a/org.argeo.cms/OSGI-INF/nodeUserAdmin.xml b/org.argeo.cms/OSGI-INF/nodeUserAdmin.xml index 268908e57..50a9ea6eb 100644 --- a/org.argeo.cms/OSGI-INF/nodeUserAdmin.xml +++ b/org.argeo.cms/OSGI-INF/nodeUserAdmin.xml @@ -1,11 +1,11 @@ - + - - - - + + + + diff --git a/org.argeo.cms/bnd.bnd b/org.argeo.cms/bnd.bnd index 52987f3da..d0047dcc6 100644 --- a/org.argeo.cms/bnd.bnd +++ b/org.argeo.cms/bnd.bnd @@ -12,7 +12,6 @@ OSGI-INF/cmsState.xml,\ OSGI-INF/simpleTransactionManager.xml,\ OSGI-INF/nodeUserAdmin.xml,\ OSGI-INF/cmsUserManager.xml,\ -OSGI-INF/deployConfig.xml,\ OSGI-INF/uuidFactory.xml,\ OSGI-INF/acrContentRepository.xml,\ OSGI-INF/cmsDeployment.xml,\ diff --git a/org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java index b5734afd3..19875e88a 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java @@ -211,7 +211,8 @@ public class RemoteSessionLoginModule implements LoginModule { if (log.isDebugEnabled()) log.debug("Client certificate " + certDn + " verified by servlet container"); } // Reverse proxy verified the client certificate - String clientDnHttpHeader = KernelUtils.getFrameworkProp(CmsConstants.HTTP_PROXY_SSL_DN); + String clientDnHttpHeader = CmsContextImpl.getCmsContext().getCmsState() + .getDeployProperty(CmsConstants.HTTP_PROXY_SSL_DN); if (clientDnHttpHeader != null) { String certDn = req.getHeader(clientDnHttpHeader); // TODO retrieve more cf. https://httpd.apache.org/docs/current/mod/mod_ssl.html diff --git a/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java b/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java index 038d7029c..d4b2f4595 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java @@ -8,16 +8,13 @@ import org.argeo.cms.ArgeoLogger; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; -import org.osgi.framework.ServiceReference; import org.osgi.service.condpermadmin.BundleLocationCondition; import org.osgi.service.condpermadmin.ConditionInfo; import org.osgi.service.condpermadmin.ConditionalPermissionAdmin; import org.osgi.service.condpermadmin.ConditionalPermissionInfo; import org.osgi.service.condpermadmin.ConditionalPermissionUpdate; -import org.osgi.service.http.HttpService; import org.osgi.service.log.LogReaderService; import org.osgi.service.permissionadmin.PermissionInfo; -import org.osgi.util.tracker.ServiceTracker; /** * Activates the kernel. Gives access to kernel information for the rest of the @@ -179,18 +176,18 @@ public class CmsActivator implements BundleActivator { // userAdminSt = new ServiceTracker<>(bundleContext, UserAdmin.class, null); // userAdminSt.open(); - ServiceTracker httpSt = new ServiceTracker(bc, HttpService.class, null) { - - @Override - public HttpService addingService(ServiceReference sr) { - Object httpPort = sr.getProperty("http.port"); - Object httpsPort = sr.getProperty("https.port"); - log.info(httpPortsMsg(httpPort, httpsPort)); - close(); - return super.addingService(sr); - } - }; - httpSt.open(); +// ServiceTracker httpSt = new ServiceTracker(bc, HttpService.class, null) { +// +// @Override +// public HttpService addingService(ServiceReference sr) { +// Object httpPort = sr.getProperty("http.port"); +// Object httpsPort = sr.getProperty("https.port"); +// log.info(httpPortsMsg(httpPort, httpsPort)); +// close(); +// return super.addingService(sr); +// } +// }; +// httpSt.open(); } private String httpPortsMsg(Object httpPort, Object httpsPort) { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/osgi/DeployConfig.java b/org.argeo.cms/src/org/argeo/cms/internal/osgi/DeployConfig.java index b8fa8a73f..205ab35de 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/osgi/DeployConfig.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/osgi/DeployConfig.java @@ -70,59 +70,50 @@ public class DeployConfig { private void setFromFrameworkProperties(boolean isFirstInit) { // user admin - List> userDirectoryConfigs = InitUtils.getUserDirectoryConfigs(); - if (userDirectoryConfigs.size() != 0) { - List activeCns = new ArrayList<>(); - for (int i = 0; i < userDirectoryConfigs.size(); i++) { - Dictionary userDirectoryConfig = userDirectoryConfigs.get(i); - String baseDn = (String) userDirectoryConfig.get(DirectoryConf.baseDn.name()); - String cn; - if (CmsConstants.ROLES_BASEDN.equals(baseDn)) - cn = ROLES; - else - cn = DirectoryConf.baseDnHash(userDirectoryConfig); - activeCns.add(cn); - userDirectoryConfig.put(CmsConstants.CN, cn); - putFactoryDeployConfig(CmsConstants.NODE_USER_ADMIN_PID, userDirectoryConfig); - } - // disable others - LdapName userAdminFactoryName = serviceFactoryDn(CmsConstants.NODE_USER_ADMIN_PID); - for (LdapName name : deployConfigs.keySet()) { - if (name.startsWith(userAdminFactoryName) && !name.equals(userAdminFactoryName)) { -// try { - Attributes attrs = deployConfigs.get(name); - String cn = name.getRdn(name.size() - 1).getValue().toString(); - if (!activeCns.contains(cn)) { - attrs.put(DirectoryConf.disabled.name(), "true"); - } -// } catch (Exception e) { -// throw new CmsException("Cannot disable user directory " + name, e); +// List> userDirectoryConfigs = InitUtils.getUserDirectoryConfigs(); +// if (userDirectoryConfigs.size() != 0) { +// List activeCns = new ArrayList<>(); +// for (int i = 0; i < userDirectoryConfigs.size(); i++) { +// Dictionary userDirectoryConfig = userDirectoryConfigs.get(i); +// String baseDn = (String) userDirectoryConfig.get(DirectoryConf.baseDn.name()); +// String cn; +// if (CmsConstants.ROLES_BASEDN.equals(baseDn)) +// cn = ROLES; +// else +// cn = DirectoryConf.baseDnHash(userDirectoryConfig); +// activeCns.add(cn); +// userDirectoryConfig.put(CmsConstants.CN, cn); +// putFactoryDeployConfig(CmsConstants.NODE_USER_ADMIN_PID, userDirectoryConfig); +// } +// // disable others +// LdapName userAdminFactoryName = serviceFactoryDn(CmsConstants.NODE_USER_ADMIN_PID); +// for (LdapName name : deployConfigs.keySet()) { +// if (name.startsWith(userAdminFactoryName) && !name.equals(userAdminFactoryName)) { +//// try { +// Attributes attrs = deployConfigs.get(name); +// String cn = name.getRdn(name.size() - 1).getValue().toString(); +// if (!activeCns.contains(cn)) { +// attrs.put(DirectoryConf.disabled.name(), "true"); // } - } - } - } +//// } catch (Exception e) { +//// throw new CmsException("Cannot disable user directory " + name, e); +//// } +// } +// } +// } // http server - Dictionary webServerConfig = InitUtils - .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT)); - if (!webServerConfig.isEmpty()) { - // TODO check for other customizers -// webServerConfig.put("customizer.class", "org.argeo.equinox.jetty.CmsJettyCustomizer"); - putFactoryDeployConfig(KernelConstants.JETTY_FACTORY_PID, webServerConfig); - } -// LdapName defaultHttpServiceDn = serviceDn(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT); -// if (deployConfigs.containsKey(defaultHttpServiceDn)) { -// // remove old default configs since we have now to start Jetty servlet bridge -// // indirectly -// deployConfigs.remove(defaultHttpServiceDn); +// Dictionary webServerConfig = InitUtils +// .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT)); +// if (!webServerConfig.isEmpty()) { +// // TODO check for other customizers +// putFactoryDeployConfig(KernelConstants.JETTY_FACTORY_PID, webServerConfig); // } // SAVE save(); // -// Dictionary webServerConfig = InitUtils -// .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT)); } public void start() { 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 44a2866f8..5ba811c92 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 @@ -19,28 +19,28 @@ public class CmsDeploymentImpl implements CmsDeployment { private HttpService httpService; private CmsState cmsState; - private DeployConfig deployConfig; +// private DeployConfig deployConfig; public void start() { - httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null; - if (deployConfig.hasDomain()) { - loadIpaJaasConfiguration(); - } +// httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null; +// if (deployConfig.hasDomain()) { +// loadIpaJaasConfiguration(); +// } } - 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 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); @@ -56,14 +56,14 @@ public class CmsDeploymentImpl implements CmsDeployment { } public void stop() { - if (deployConfig != null) { - deployConfig.save(); - } +// if (deployConfig != null) { +// deployConfig.save(); +// } } - public void setDeployConfig(DeployConfig deployConfig) { - this.deployConfig = deployConfig; - } +// public void setDeployConfig(DeployConfig deployConfig) { +// this.deployConfig = deployConfig; +// } public void setCmsState(CmsState cmsState) { this.cmsState = cmsState; 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 41e326534..ae85a2fc8 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 @@ -3,6 +3,7 @@ package org.argeo.cms.internal.runtime; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; +import java.nio.file.Path; import java.util.UUID; import javax.security.auth.login.Configuration; @@ -81,6 +82,16 @@ public class CmsStateImpl implements CmsState { log.info("## ARGEO CMS STOPPED after " + (duration / 60) + "h " + (duration % 60) + "min uptime ##"); } + @Override + public String getDeployProperty(String key) { + return KernelUtils.getFrameworkProp(key); + } + + @Override + public Path getDataPath(String relativePath) { + return KernelUtils.getOsgiInstancePath(relativePath); + } + @Override public Long getAvailableSince() { return availableSince; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java index 64e32b16a..18a880e31 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java @@ -5,12 +5,14 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Dictionary; import java.util.Iterator; +import java.util.List; import java.util.Optional; import java.util.Set; @@ -31,10 +33,11 @@ import org.apache.commons.httpclient.params.HttpParams; import org.argeo.api.cms.CmsAuth; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.CmsState; import org.argeo.cms.internal.http.client.HttpCredentialProvider; import org.argeo.cms.internal.http.client.SpnegoAuthScheme; -import org.argeo.osgi.useradmin.DirectoryUserAdmin; import org.argeo.osgi.useradmin.AggregatingUserAdmin; +import org.argeo.osgi.useradmin.DirectoryUserAdmin; import org.argeo.osgi.useradmin.UserDirectory; import org.argeo.util.directory.DirectoryConf; import org.argeo.util.naming.dns.DnsBrowser; @@ -65,14 +68,27 @@ public class CmsUserAdmin extends AggregatingUserAdmin { private WorkControl transactionManager; private WorkTransaction userTransaction; + private CmsState cmsState; + public CmsUserAdmin() { super(CmsConstants.ROLES_BASEDN, CmsConstants.TOKENS_BASEDN); } public void start() { + super.start(); + List> configs = InitUtils.getUserDirectoryConfigs(); + for (Dictionary config : configs) { + UserDirectory userDirectory = enableUserDirectory(config); + if (userDirectory.getRealm().isPresent()) + loadIpaJaasConfiguration(); + } } public void stop() { +// for (UserDirectory userDirectory : getUserDirectories()) { +// removeUserDirectory(userDirectory); +// } + super.stop(); } public UserDirectory enableUserDirectory(Dictionary properties) { @@ -206,6 +222,15 @@ public class CmsUserAdmin extends AggregatingUserAdmin { } } + 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."); + } + } + private String getKerberosServicePrincipal(String realm) { String hostname; try (DnsBrowser dnsBrowser = new DnsBrowser()) { @@ -288,8 +313,8 @@ public class CmsUserAdmin extends AggregatingUserAdmin { this.userTransaction = userTransaction; } - /* - * STATIC - */ + public void setCmsState(CmsState cmsState) { + this.cmsState = cmsState; + } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/InitUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/InitUtils.java index 986f7914d..f634e43b5 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/InitUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/InitUtils.java @@ -5,25 +5,14 @@ import static org.argeo.cms.internal.runtime.KernelUtils.getFrameworkProp; import java.io.File; import java.io.FileFilter; import java.io.IOException; -import java.io.Reader; -import java.net.InetAddress; import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.KeyStore; import java.util.ArrayList; -import java.util.Arrays; import java.util.Dictionary; -import java.util.Hashtable; import java.util.List; -import javax.security.auth.x500.X500Principal; - import org.apache.commons.io.FileUtils; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsLog; -import org.argeo.cms.internal.http.InternalHttpConstants; import org.argeo.util.directory.DirectoryConf; /** @@ -34,92 +23,92 @@ public class InitUtils { private final static CmsLog log = CmsLog.getLog(InitUtils.class); /** Override the provided config with the framework properties */ - public static Dictionary getHttpServerConfig(Dictionary provided) { - String httpPort = getFrameworkProp("org.osgi.service.http.port"); - String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure"); - /// TODO make it more generic - String httpHost = getFrameworkProp( - InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTP_HOST); - String httpsHost = getFrameworkProp( - InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTPS_HOST); - String webSocketEnabled = getFrameworkProp( - InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.WEBSOCKET_ENABLED); - - final Hashtable props = new Hashtable(); - // try { - if (httpPort != null || httpsPort != null) { - boolean httpEnabled = httpPort != null; - props.put(InternalHttpConstants.HTTP_ENABLED, httpEnabled); - boolean httpsEnabled = httpsPort != null; - props.put(InternalHttpConstants.HTTPS_ENABLED, httpsEnabled); - - if (httpEnabled) { - props.put(InternalHttpConstants.HTTP_PORT, httpPort); - if (httpHost != null) - props.put(InternalHttpConstants.HTTP_HOST, httpHost); - } - - if (httpsEnabled) { - props.put(InternalHttpConstants.HTTPS_PORT, httpsPort); - if (httpsHost != null) - props.put(InternalHttpConstants.HTTPS_HOST, httpsHost); - - // server certificate - Path keyStorePath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_KEYSTORE_PATH); - Path pemKeyPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_PEM_KEY_PATH); - Path pemCertPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_PEM_CERT_PATH); - String keyStorePasswordStr = getFrameworkProp( - InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_PASSWORD); - char[] keyStorePassword; - if (keyStorePasswordStr == null) - keyStorePassword = "changeit".toCharArray(); - else - keyStorePassword = keyStorePasswordStr.toCharArray(); - - // if PEM files both exists, update the PKCS12 file - if (Files.exists(pemCertPath) && Files.exists(pemKeyPath)) { - // TODO check certificate update time? monitor changes? - KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12); - try (Reader key = Files.newBufferedReader(pemKeyPath, StandardCharsets.US_ASCII); - Reader cert = Files.newBufferedReader(pemCertPath, StandardCharsets.US_ASCII);) { - PkiUtils.loadPem(keyStore, key, keyStorePassword, cert); - PkiUtils.saveKeyStore(keyStorePath, keyStorePassword, keyStore); - if (log.isDebugEnabled()) - log.debug("PEM certificate stored in " + keyStorePath); - } catch (IOException e) { - log.error("Cannot read PEM files " + pemKeyPath + " and " + pemCertPath, e); - } - } - - if (!Files.exists(keyStorePath)) - createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12); - props.put(InternalHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12); - props.put(InternalHttpConstants.SSL_KEYSTORE, keyStorePath.toString()); - props.put(InternalHttpConstants.SSL_PASSWORD, new String(keyStorePassword)); - -// props.put(InternalHttpConstants.SSL_KEYSTORETYPE, "PKCS11"); -// props.put(InternalHttpConstants.SSL_KEYSTORE, "../../nssdb"); -// props.put(InternalHttpConstants.SSL_PASSWORD, keyStorePassword); - - // client certificate authentication - String wantClientAuth = getFrameworkProp( - InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_WANTCLIENTAUTH); - if (wantClientAuth != null) - props.put(InternalHttpConstants.SSL_WANTCLIENTAUTH, Boolean.parseBoolean(wantClientAuth)); - String needClientAuth = getFrameworkProp( - InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_NEEDCLIENTAUTH); - if (needClientAuth != null) - props.put(InternalHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth)); - } - - // web socket - if (webSocketEnabled != null && webSocketEnabled.equals("true")) - props.put(InternalHttpConstants.WEBSOCKET_ENABLED, true); - - props.put(CmsConstants.CN, CmsConstants.DEFAULT); - } - return props; - } +// public static Dictionary getHttpServerConfig(Dictionary provided) { +// String httpPort = getFrameworkProp("org.osgi.service.http.port"); +// String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure"); +// /// TODO make it more generic +// String httpHost = getFrameworkProp( +// InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTP_HOST); +// String httpsHost = getFrameworkProp( +// InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTPS_HOST); +// String webSocketEnabled = getFrameworkProp( +// InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.WEBSOCKET_ENABLED); +// +// final Hashtable props = new Hashtable(); +// // try { +// if (httpPort != null || httpsPort != null) { +// boolean httpEnabled = httpPort != null; +// props.put(InternalHttpConstants.HTTP_ENABLED, httpEnabled); +// boolean httpsEnabled = httpsPort != null; +// props.put(InternalHttpConstants.HTTPS_ENABLED, httpsEnabled); +// +// if (httpEnabled) { +// props.put(InternalHttpConstants.HTTP_PORT, httpPort); +// if (httpHost != null) +// props.put(InternalHttpConstants.HTTP_HOST, httpHost); +// } +// +// if (httpsEnabled) { +// props.put(InternalHttpConstants.HTTPS_PORT, httpsPort); +// if (httpsHost != null) +// props.put(InternalHttpConstants.HTTPS_HOST, httpsHost); +// +// // server certificate +// Path keyStorePath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_KEYSTORE_PATH); +// Path pemKeyPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_PEM_KEY_PATH); +// Path pemCertPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_PEM_CERT_PATH); +// String keyStorePasswordStr = getFrameworkProp( +// InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_PASSWORD); +// char[] keyStorePassword; +// if (keyStorePasswordStr == null) +// keyStorePassword = "changeit".toCharArray(); +// else +// keyStorePassword = keyStorePasswordStr.toCharArray(); +// +// // if PEM files both exists, update the PKCS12 file +// if (Files.exists(pemCertPath) && Files.exists(pemKeyPath)) { +// // TODO check certificate update time? monitor changes? +// KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12); +// try (Reader key = Files.newBufferedReader(pemKeyPath, StandardCharsets.US_ASCII); +// Reader cert = Files.newBufferedReader(pemCertPath, StandardCharsets.US_ASCII);) { +// PkiUtils.loadPem(keyStore, key, keyStorePassword, cert); +// PkiUtils.saveKeyStore(keyStorePath, keyStorePassword, keyStore); +// if (log.isDebugEnabled()) +// log.debug("PEM certificate stored in " + keyStorePath); +// } catch (IOException e) { +// log.error("Cannot read PEM files " + pemKeyPath + " and " + pemCertPath, e); +// } +// } +// +// if (!Files.exists(keyStorePath)) +// createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12); +// props.put(InternalHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12); +// props.put(InternalHttpConstants.SSL_KEYSTORE, keyStorePath.toString()); +// props.put(InternalHttpConstants.SSL_PASSWORD, new String(keyStorePassword)); +// +//// props.put(InternalHttpConstants.SSL_KEYSTORETYPE, "PKCS11"); +//// props.put(InternalHttpConstants.SSL_KEYSTORE, "../../nssdb"); +//// props.put(InternalHttpConstants.SSL_PASSWORD, keyStorePassword); +// +// // client certificate authentication +// String wantClientAuth = getFrameworkProp( +// InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_WANTCLIENTAUTH); +// if (wantClientAuth != null) +// props.put(InternalHttpConstants.SSL_WANTCLIENTAUTH, Boolean.parseBoolean(wantClientAuth)); +// String needClientAuth = getFrameworkProp( +// InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_NEEDCLIENTAUTH); +// if (needClientAuth != null) +// props.put(InternalHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth)); +// } +// +// // web socket +// if (webSocketEnabled != null && webSocketEnabled.equals("true")) +// props.put(InternalHttpConstants.WEBSOCKET_ENABLED, true); +// +// props.put(CmsConstants.CN, CmsConstants.DEFAULT); +// } +// return props; +// } public static List> getUserDirectoryConfigs() { List> res = new ArrayList<>(); @@ -258,33 +247,4 @@ public class InitUtils { } } - private static void createSelfSignedKeyStore(Path keyStorePath, char[] keyStorePassword, String keyStoreType) { - // for (Provider provider : Security.getProviders()) - // System.out.println(provider.getName()); -// File keyStoreFile = keyStorePath.toFile(); - char[] keyPwd = Arrays.copyOf(keyStorePassword, keyStorePassword.length); - if (!Files.exists(keyStorePath)) { - try { - Files.createDirectories(keyStorePath.getParent()); - KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword, keyStoreType); - PkiUtils.generateSelfSignedCertificate(keyStore, - new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"), - 1024, keyPwd); - PkiUtils.saveKeyStore(keyStorePath, keyStorePassword, keyStore); - if (log.isDebugEnabled()) - log.debug("Created self-signed unsecure keystore " + keyStorePath); - } catch (Exception e) { - try { - if (Files.size(keyStorePath) == 0) - Files.delete(keyStorePath); - } catch (IOException e1) { - // silent - } - log.error("Cannot create keystore " + keyStorePath, e); - } - } else { - throw new IllegalStateException("Keystore " + keyStorePath + " already exists"); - } - } - } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelConstants.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelConstants.java index dfe86cfaa..a02c557f2 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelConstants.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelConstants.java @@ -12,9 +12,6 @@ public interface KernelConstants { // Files String DEPLOY_CONFIG_PATH = DIR_NODE + '/' + CmsConstants.DEPLOY_BASEDN + ".ldif"; - String DEFAULT_KEYSTORE_PATH = DIR_NODE + '/' + CmsConstants.NODE + ".p12"; - String DEFAULT_PEM_KEY_PATH = DIR_NODE + '/' + CmsConstants.NODE + ".key"; - String DEFAULT_PEM_CERT_PATH = DIR_NODE + '/' + CmsConstants.NODE + ".crt"; String NODE_KEY_TAB_PATH = DIR_NODE + "/krb5.keytab"; // Security diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelUtils.java index 60c796af7..5635bccfe 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelUtils.java @@ -101,7 +101,7 @@ public class KernelUtils implements KernelConstants { return value; } - public static String getFrameworkProp(String key) { + static String getFrameworkProp(String key) { return getFrameworkProp(key, null); } diff --git a/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java b/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java index a868e8c26..916a413a9 100644 --- a/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java +++ b/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java @@ -55,12 +55,12 @@ public class StaticCms { .build(register); // Deployment Configuration - DeployConfig deployConfig = new DeployConfig(); - Component deployConfigC = new Component.Builder<>(deployConfig) // - .addType(DeployConfig.class) // - .addActivation(deployConfig::start) // - .addDeactivation(deployConfig::stop) // - .build(register); +// DeployConfig deployConfig = new DeployConfig(); +// Component deployConfigC = new Component.Builder<>(deployConfig) // +// .addType(DeployConfig.class) // +// .addActivation(deployConfig::start) // +// .addDeactivation(deployConfig::stop) // +// .build(register); // CMS Deployment CmsDeploymentImpl cmsDeployment = new CmsDeploymentImpl(); @@ -69,7 +69,7 @@ public class StaticCms { .addActivation(cmsDeployment::start) // .addDeactivation(cmsDeployment::stop) // .addDependency(cmsStateC.getType(CmsState.class), cmsDeployment::setCmsState, null) // - .addDependency(deployConfigC.getType(DeployConfig.class), cmsDeployment::setDeployConfig, null) // +// .addDependency(deployConfigC.getType(DeployConfig.class), cmsDeployment::setDeployConfig, null) // .build(register); // Transaction manager @@ -85,10 +85,10 @@ public class StaticCms { .addType(UserAdmin.class) // .addDependency(transactionManagerC.getType(WorkControl.class), userAdmin::setTransactionManager, null) // .addDependency(transactionManagerC.getType(WorkTransaction.class), userAdmin::setUserTransaction, null) // - .addDependency(deployConfigC.getType(DeployConfig.class), (d) -> { - for (Dictionary userDirectoryConfig : d.getUserDirectoryConfigs()) - userAdmin.enableUserDirectory(userDirectoryConfig); - }, null) // +// .addDependency(deployConfigC.getType(DeployConfig.class), (d) -> { +// for (Dictionary userDirectoryConfig : d.getUserDirectoryConfigs()) +// userAdmin.enableUserDirectory(userDirectoryConfig); +// }, null) // .build(register); // User manager diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/PkiUtils.java b/org.argeo.cms/src/org/argeo/cms/security/PkiUtils.java similarity index 84% rename from org.argeo.cms/src/org/argeo/cms/internal/runtime/PkiUtils.java rename to org.argeo.cms/src/org/argeo/cms/security/PkiUtils.java index 474a89950..d6f90acfb 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/PkiUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/security/PkiUtils.java @@ -1,10 +1,11 @@ -package org.argeo.cms.internal.runtime; +package org.argeo.cms.security; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.math.BigInteger; +import java.net.InetAddress; import java.nio.file.Files; import java.nio.file.Path; import java.security.GeneralSecurityException; @@ -18,10 +19,14 @@ import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Date; import javax.security.auth.x500.X500Principal; +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.internal.runtime.KernelConstants; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; @@ -42,8 +47,15 @@ import org.bouncycastle.pkcs.PKCSException; * Utilities around private keys and certificate, mostly wrapping BouncyCastle * implementations. */ -class PkiUtils { - final static String PKCS12 = "PKCS12"; +public class PkiUtils { + private final static CmsLog log = CmsLog.getLog(PkiUtils.class); + + public final static String PKCS12 = "PKCS12"; + public static final String DEFAULT_KEYSTORE_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".p12"; + + public static final String DEFAULT_PEM_KEY_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".key"; + + public static final String DEFAULT_PEM_CERT_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".crt"; private final static String SECURITY_PROVIDER; static { @@ -256,4 +268,33 @@ class PkiUtils { } + public static void createSelfSignedKeyStore(Path keyStorePath, char[] keyStorePassword, String keyStoreType) { + // for (Provider provider : Security.getProviders()) + // System.out.println(provider.getName()); + // File keyStoreFile = keyStorePath.toFile(); + char[] keyPwd = Arrays.copyOf(keyStorePassword, keyStorePassword.length); + if (!Files.exists(keyStorePath)) { + try { + Files.createDirectories(keyStorePath.getParent()); + KeyStore keyStore = getKeyStore(keyStorePath, keyStorePassword, keyStoreType); + generateSelfSignedCertificate(keyStore, + new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"), + 1024, keyPwd); + saveKeyStore(keyStorePath, keyStorePassword, keyStore); + if (log.isDebugEnabled()) + log.debug("Created self-signed unsecure keystore " + keyStorePath); + } catch (Exception e) { + try { + if (Files.size(keyStorePath) == 0) + Files.delete(keyStorePath); + } catch (IOException e1) { + // silent + } + log.error("Cannot create keystore " + keyStorePath, e); + } + } else { + throw new IllegalStateException("Keystore " + keyStorePath + " already exists"); + } + } + } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java index 3857b08d0..79d2bd3cb 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -238,7 +238,11 @@ public class AggregatingUserAdmin implements UserAdmin { // return res; // } - public void destroy() { + public void start() { + + } + + public void stop() { for (LdapName name : businessRoles.keySet()) { DirectoryUserAdmin userDirectory = businessRoles.get(name); destroy(userDirectory); @@ -254,6 +258,14 @@ public class AggregatingUserAdmin implements UserAdmin { userDirectory.destroy(); } +// protected void removeUserDirectory(UserDirectory userDirectory) { +// LdapName baseDn = toLdapName(userDirectory.getContext()); +// businessRoles.remove(baseDn); +// if (userDirectory instanceof DirectoryUserAdmin) +// destroy((DirectoryUserAdmin) userDirectory); +// } + + @Deprecated protected void removeUserDirectory(String basePath) { if (isSystemRolesBaseDn(basePath)) throw new IllegalArgumentException("System roles cannot be removed "); @@ -274,6 +286,8 @@ public class AggregatingUserAdmin implements UserAdmin { public Set getUserDirectories() { TreeSet res = new TreeSet<>((o1, o2) -> o1.getContext().compareTo(o2.getContext())); res.addAll(businessRoles.values()); + res.add(systemRoles); return res; } + } -- 2.30.2