Working CMS Kernel
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 25 Jan 2015 21:39:11 +0000 (21:39 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 25 Jan 2015 21:39:11 +0000 (21:39 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@7697 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

19 files changed:
org.argeo.cms/.settings/org.eclipse.pde.core.prefs
org.argeo.cms/META-INF/spring/backend.xml [deleted file]
org.argeo.cms/META-INF/spring/osgi.xml [deleted file]
org.argeo.cms/bnd.bnd
org.argeo.cms/build.properties
org.argeo.cms/pom.xml
org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitNode.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitNodeTypes.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttpFilter.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/jackrabbit-node.properties [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/repository-h2.xml [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/repository-memory.xml [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/repository-postgresql.xml [new file with mode: 0644]

index f29e940a0059cdd3c6d5a8fefdc84498c389f78d..4b08937777f177e10790f7e74539792cf1a4add8 100644 (file)
@@ -1,3 +1,2 @@
 eclipse.preferences.version=1
 pluginProject.extensions=false
-resolve.requirebundle=false
diff --git a/org.argeo.cms/META-INF/spring/backend.xml b/org.argeo.cms/META-INF/spring/backend.xml
deleted file mode 100644 (file)
index c32676f..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:p="http://www.springframework.org/schema/p"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans
-        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
-        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
-
-       <!-- BACKEND -->
-
-       <bean id="cmsRepository" class="org.argeo.jackrabbit.JackrabbitWrapper"
-               init-method="init" destroy-method="destroy">
-               <property name="cndFiles">
-                       <list>
-                               <value>/org/argeo/cms/cms.cnd</value>
-                       </list>
-               </property>
-               <property name="repository" ref="repository" />
-               <property name="bundleContext" ref="bundleContext" />
-       </bean>
-
-       <!-- Execute initialization with a system authentication -->
-       <bean
-               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
-               <property name="authenticationManager" ref="authenticationManager" />
-               <property name="beanNames">
-                       <list>
-                               <value>cmsRepository</value>
-                       </list>
-               </property>
-       </bean>
-</beans>
\ No newline at end of file
diff --git a/org.argeo.cms/META-INF/spring/osgi.xml b/org.argeo.cms/META-INF/spring/osgi.xml
deleted file mode 100644 (file)
index 48609a1..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
-       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
-       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
-       http://www.springframework.org/schema/beans   \r
-       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
-\r
-       <!-- REFERENCES -->\r
-       <reference id="repository" interface="javax.jcr.Repository"\r
-               filter="(argeo.jcr.repository.alias=node)" />\r
-\r
-       <reference id="authenticationManager"\r
-               interface="org.springframework.security.authentication.AuthenticationManager" />\r
-\r
-       <!-- SERVICES -->\r
-       <service ref="cmsRepository" interface="javax.jcr.Repository">\r
-               <service-properties>\r
-                       <beans:entry key="argeo.jcr.repository.alias" value="cms" />\r
-               </service-properties>\r
-       </service>\r
-</beans:beans>
\ No newline at end of file
index 48343c42d87bffe3e87e658984b90daafa78c14b..825c5957f169e4ea24bedb05b8be79c2b210c993 100644 (file)
@@ -1,10 +1,15 @@
+Bundle-Activator: org.argeo.cms.internal.kernel.Activator
 Import-Package: org.springframework.core,\
-                               org.springframework.dao,\
-                               org.eclipse.core.commands,\
-                               org.eclipse.swt,\
-                               org.eclipse.jface.window,\
-                               javax.jcr.security,\
-                               org.eclipse.swt.widgets;version="0.0.0",\
-                               org.eclipse.rap.*;resolution:=optional,\
-                               org.argeo.jackrabbit.*;resolution:=optional,\
-                               *
+org.springframework.dao,\
+org.eclipse.core.commands,\
+org.eclipse.swt,\
+org.eclipse.jface.window,\
+javax.jcr.security,\
+org.xml.sax;version="0.0.0",\
+org.eclipse.swt.widgets;version="0.0.0",\
+org.argeo.jcr,\
+org.h2;resolution:=optional,\
+org.springframework.context,\
+org.apache.jackrabbit.api,\
+org.apache.jackrabbit.commons,\
+*
index 34d2e4d2dad529ceaeb953bfcdb63c51d69ffed2..cee30655ba224c455827faaf55a7bc98e9836e26 100644 (file)
@@ -2,3 +2,4 @@ source.. = src/
 output.. = bin/
 bin.includes = META-INF/,\
                .
+additional.bundles = org.apache.jackrabbit.data
index f0585b06f3dfd1c1bfbe75cbd23ab0c5756d9c9b..c01e0161d0f9739f1b365cd9e845ffbe40f85f8e 100644 (file)
                        <artifactId>org.argeo.server.jcr</artifactId>
                        <version>2.1.13-SNAPSHOT</version>
                </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.server.jackrabbit</artifactId>
+                       <version>2.1.13-SNAPSHOT</version>
+               </dependency>
 
                <dependency>
                        <groupId>org.argeo.commons</groupId>
index 6043980e90f8b58a927e45da360027dab298cad2..6722d7b86291051c61b39fc286f88d136ebc3a55 100644 (file)
@@ -19,12 +19,14 @@ import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
 import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
+import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 
 /** Manages history and navigation */
 public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint
                implements CmsSession {
        private final Log log = LogFactory.getLog(AbstractCmsEntryPoint.class);
+       private static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
 
        private Repository repository;
        private String workspace;
@@ -39,8 +41,15 @@ public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint
        private BrowserNavigation history;
 
        public AbstractCmsEntryPoint(Repository repository, String workspace) {
-               if (SecurityContextHolder.getContext().getAuthentication() == null)
-                       logAsAnonymous();
+               if (SecurityContextHolder.getContext().getAuthentication() == null) {
+                       SecurityContext contextFromSessionObject = (SecurityContext) RWT
+                                       .getRequest().getSession()
+                                       .getAttribute(SPRING_SECURITY_CONTEXT_KEY);
+                       if (contextFromSessionObject != null)
+                               SecurityContextHolder.setContext(contextFromSessionObject);
+                       else
+                               logAsAnonymous();
+               }
 
                this.repository = repository;
                this.workspace = workspace;
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java
new file mode 100644 (file)
index 0000000..4d166ea
--- /dev/null
@@ -0,0 +1,22 @@
+package org.argeo.cms.internal.kernel;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class Activator implements BundleActivator {
+       private Kernel kernel;
+
+       @Override
+       public void start(BundleContext context) throws Exception {
+               assert kernel == null;
+               kernel = new Kernel(context);
+               kernel.init();
+       }
+
+       @Override
+       public void stop(BundleContext context) throws Exception {
+               kernel.destroy();
+               kernel = null;
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitNode.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitNode.java
new file mode 100644 (file)
index 0000000..e36f713
--- /dev/null
@@ -0,0 +1,133 @@
+package org.argeo.cms.internal.kernel;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import javax.jcr.Repository;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
+import org.argeo.ArgeoException;
+import org.argeo.jackrabbit.JackrabbitWrapper;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.xml.sax.InputSource;
+
+/** Data storage */
+class JackrabbitNode extends JackrabbitWrapper {
+       private static Log log = LogFactory.getLog(JackrabbitNode.class);
+
+       private ServiceRegistration<Repository> repositoryReg;
+
+       public JackrabbitNode(BundleContext bundleContext) {
+               setBundleContext(bundleContext);
+               createNode();
+               setCndFiles(Arrays.asList(KernelConstants.DEFAULT_CNDS));
+               prepareDataModel();
+       }
+
+       public void publish() {
+               Hashtable<String, String> regProps = new Hashtable<String, String>();
+               regProps.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS,
+                               ArgeoJcrConstants.ALIAS_NODE);
+               repositoryReg = getBundleContext().registerService(Repository.class,
+                               this, regProps);
+       }
+
+       public void destroy() {
+               repositoryReg.unregister();
+               ((RepositoryImpl) getRepository()).shutdown();
+       }
+
+       Dictionary<String, ?> getDefaults() {
+               return KernelUtils.asDictionary(getClass().getClassLoader(),
+                               "/org/argeo/cms/internal/kernel/jackrabbit-node.properties");
+       }
+
+       InputSource getConfigurationXml(JackrabbitNodeTypes type) {
+               ClassLoader cl = getClass().getClassLoader();
+               InputStream in = cl
+                               .getResourceAsStream("/org/argeo/cms/internal/kernel/repository-h2.xml");
+               return new InputSource(in);
+       }
+
+       Properties getDefaultConfigurationProperties() {
+               Properties configurationProperties = new Properties();
+               configurationProperties.setProperty(KernelConstants.REPO_DBUSER, "sa");
+               configurationProperties
+                               .setProperty(KernelConstants.REPO_DBPASSWORD, "");
+               configurationProperties.setProperty(KernelConstants.REPO_MAX_POOL_SIZE,
+                               "10");
+               configurationProperties.setProperty(
+                               KernelConstants.REPO_DEFAULT_WORKSPACE, "main");
+               return configurationProperties;
+       }
+
+       private void createNode() {
+               Thread.currentThread().setContextClassLoader(
+                               getClass().getClassLoader());
+
+               File osgiInstanceDir = KernelUtils
+                               .getOsgiInstanceDir(getBundleContext());
+               File homeDir = new File(osgiInstanceDir, "node");
+
+               // H2
+               String dburl = "jdbc:h2:" + homeDir.getPath() + "/h2/repository";
+               Properties configurationProperties = getDefaultConfigurationProperties();
+               configurationProperties.setProperty(KernelConstants.REPO_DBURL, dburl);
+               configurationProperties.put(
+                               RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
+                               homeDir.getAbsolutePath());
+               // InputSource configurationXml = getConfigurationXml(null);
+
+               // jackrabbitContainer.setHomeDirectory(homeDir);
+               // jackrabbitContainer.setConfigurationProperties(configurationProperties);
+               // jackrabbitContainer.setConfigurationXml(configurationXml);
+
+               // jackrabbitContainer.init();
+
+               RepositoryImpl repository = createJackrabbitRepository(
+                               configurationProperties, getConfigurationXml(null));
+
+               setRepository(repository);
+       }
+
+       private RepositoryImpl createJackrabbitRepository(Properties vars,
+                       InputSource config) {
+               File homeDirectory = null;
+               long begin = System.currentTimeMillis();
+               InputStream configurationIn = null;
+               RepositoryImpl repository;
+               try {
+                       RepositoryConfig repositoryConfig = RepositoryConfig.create(config,
+                                       vars);
+
+                       //
+                       // Actual repository creation
+                       //
+                       repository = RepositoryImpl.create(repositoryConfig);
+
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                       if (log.isTraceEnabled())
+                               log.trace("Created Jackrabbit repository in " + duration
+                                               + " s, home: " + homeDirectory);
+
+                       return repository;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot create Jackrabbit repository "
+                                       + homeDirectory, e);
+               } finally {
+                       IOUtils.closeQuietly(configurationIn);
+               }
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitNodeTypes.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitNodeTypes.java
new file mode 100644 (file)
index 0000000..1a84cae
--- /dev/null
@@ -0,0 +1,5 @@
+package org.argeo.cms.internal.kernel;
+
+enum JackrabbitNodeTypes {
+       NOT_CONFIGURED, h2, postgresql, memory;
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java
new file mode 100644 (file)
index 0000000..c6a1630
--- /dev/null
@@ -0,0 +1,78 @@
+package org.argeo.cms.internal.kernel;
+
+import javax.jcr.RepositoryFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory;
+import org.argeo.security.core.InternalAuthentication;
+import org.osgi.framework.BundleContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ * Argeo CMS Kernel. Responsible for :
+ * <ul>
+ * <li>security</li>
+ * <li>provisioning</li>
+ * <li>transaction</li>
+ * <li>logging</li>
+ * <li>local and remote file systems access</li>
+ * <li>OS access</li>
+ * </ul>
+ */
+final class Kernel {
+       private final static Log log = LogFactory.getLog(Kernel.class);
+
+       private final BundleContext bundleContext;
+
+       private JackrabbitNode node;
+       private OsgiJackrabbitRepositoryFactory repositoryFactory;
+       private NodeSecurity nodeSecurity;
+       private NodeHttpFilter httpFilter;
+
+       Kernel(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       void init() {
+               long begin = System.currentTimeMillis();
+               InternalAuthentication initAuth = new InternalAuthentication(
+                               KernelConstants.DEFAULT_SECURITY_KEY);
+               SecurityContextHolder.getContext().setAuthentication(initAuth);
+
+               try {
+                       node = new JackrabbitNode(bundleContext);
+                       repositoryFactory = new OsgiJackrabbitRepositoryFactory();
+                       nodeSecurity = new NodeSecurity(bundleContext, node);
+                       httpFilter = new NodeHttpFilter(bundleContext, nodeSecurity);
+
+                       // Publish services to OSGi register
+                       nodeSecurity.publish();
+                       node.publish();
+                       bundleContext.registerService(RepositoryFactory.class,
+                                       repositoryFactory, null);
+                       httpFilter.publish();
+               } catch (Exception e) {
+                       log.error("Cannot initialize Argeo CMS", e);
+                       throw new ArgeoException("Cannot initialize", e);
+               }
+
+               long duration = System.currentTimeMillis() - begin;
+               log.info("## ARGEO CMS UP in " + (duration / 1000) + "."
+                               + (duration % 1000) + "s ##");
+       }
+
+       void destroy() {
+               long begin = System.currentTimeMillis();
+
+               httpFilter = null;
+               nodeSecurity.destroy();
+               node.destroy();
+
+               long duration = System.currentTimeMillis() - begin;
+               log.info("## ARGEO CMS DOWN in " + (duration / 1000) + "."
+                               + (duration % 1000) + "s ##");
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java
new file mode 100644 (file)
index 0000000..d36f7a5
--- /dev/null
@@ -0,0 +1,18 @@
+package org.argeo.cms.internal.kernel;
+
+interface KernelConstants {
+       // Node
+       final static String REPO_HOME = "argeo.node.repo.home";
+       final static String REPO_CONFIGURATION = "argeo.node.repo.configuration";
+       final static String REPO_DEFAULT_WORKSPACE = "argeo.node.repo.defaultWorkspace";
+       final static String REPO_DBURL = "argeo.node.repo.dburl";
+       final static String REPO_DBUSER = "argeo.node.repo.dbuser";
+       final static String REPO_DBPASSWORD = "argeo.node.repo.dbpassword";
+       final static String REPO_MAX_POOL_SIZE = "argeo.node.repo.maxPoolSize";
+
+       final static String[] DEFAULT_CNDS = { "/org/argeo/jcr/argeo.cnd",
+                       "/org/argeo/cms/cms.cnd" };
+       
+       // Security
+       final static String DEFAULT_SECURITY_KEY = "argeo";
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
new file mode 100644 (file)
index 0000000..c3297cb
--- /dev/null
@@ -0,0 +1,43 @@
+package org.argeo.cms.internal.kernel;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.argeo.cms.CmsException;
+import org.osgi.framework.BundleContext;
+
+class KernelUtils {
+       final static String OSGI_INSTANCE_AREA = "osgi.instance.area";
+
+       static Dictionary<String, ?> asDictionary(Properties props) {
+               Hashtable<String, Object> hashtable = new Hashtable<String, Object>();
+               for (Object key : props.keySet()) {
+                       hashtable.put(key.toString(), props.get(key));
+               }
+               return hashtable;
+       }
+
+       static Dictionary<String, ?> asDictionary(ClassLoader cl,
+                       String resource) {
+               Properties props = new Properties();
+               try {
+                       props.load(cl.getResourceAsStream(resource));
+               } catch (IOException e) {
+                       throw new CmsException("Cannot load " + resource
+                                       + " from classpath", e);
+               }
+               return asDictionary(props);
+       }
+
+       static File getOsgiInstanceDir(BundleContext bundleContext) {
+               return new File(bundleContext.getProperty(OSGI_INSTANCE_AREA)
+                               .substring("file:".length())).getAbsoluteFile();
+       }
+
+       private KernelUtils() {
+
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttpFilter.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttpFilter.java
new file mode 100644 (file)
index 0000000..a32228a
--- /dev/null
@@ -0,0 +1,158 @@
+package org.argeo.cms.internal.kernel;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.eclipse.equinox.http.servlet.ExtendedHttpService;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.HttpContext;
+import org.osgi.util.tracker.ServiceTracker;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+class NodeHttpFilter implements Filter {
+       private final static Log log = LogFactory.getLog(NodeHttpFilter.class);
+
+       private final static String ATTR_AUTH = "auth";
+       private final static String HEADER_AUTHORIZATION = "Authorization";
+
+       static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
+
+       private ExtendedHttpService httpService;
+       private final AuthenticationManager authenticationManager;
+
+       private Boolean basicAuthEnabled = false;
+
+       NodeHttpFilter(BundleContext bundleContext,
+                       AuthenticationManager authenticationManager) {
+               this.authenticationManager = authenticationManager;
+
+               // Equinox dependency
+               ServiceTracker<ExtendedHttpService, ExtendedHttpService> st = new ServiceTracker<ExtendedHttpService, ExtendedHttpService>(
+                               bundleContext, ExtendedHttpService.class, null);
+               st.open();
+               try {
+                       httpService = st.waitForService(1000);
+               } catch (InterruptedException e) {
+                       httpService = null;
+               }
+
+               if (httpService == null)
+                       throw new CmsException("Could not find "
+                                       + ExtendedHttpService.class + " service.");
+       }
+
+       void publish() {
+               try {
+                       HttpContext httpContext = httpService.createDefaultHttpContext();
+                       httpService.registerFilter("/", this, null, httpContext);
+               } catch (Exception e) {
+                       throw new CmsException("Cannot register HTTP filter", e);
+               }
+       }
+
+       @Override
+       public void doFilter(ServletRequest servletRequest,
+                       ServletResponse servletResponse, FilterChain filterChain)
+                       throws IOException, ServletException {
+               HttpServletRequest request = (HttpServletRequest) servletRequest;
+               HttpSession httpSession = request.getSession();
+
+               // Authenticate from session
+               SecurityContext contextFromSession = (SecurityContext) httpSession
+                               .getAttribute(SPRING_SECURITY_CONTEXT_KEY);
+               if (contextFromSession != null) {
+                       filterChain.doFilter(servletRequest, servletResponse);
+                       return;
+               }
+
+               if (basicAuthEnabled) {
+                       // Basic auth
+                       String basicAuth = request.getHeader(HEADER_AUTHORIZATION);
+
+                       // for (Enumeration<String> headerNames = request.getHeaderNames();
+                       // headerNames
+                       // .hasMoreElements();) {
+                       // String headerName = headerNames.nextElement();
+                       // Object headerValue = request.getHeader(headerName);
+                       // log.debug(headerName + ": " + headerValue);
+                       // }
+
+                       if (basicAuth == null) {
+                               HttpServletResponse response = (HttpServletResponse) servletResponse;
+                               response.setStatus(401);
+                               response.setHeader("WWW-Authenticate", "basic realm=\"Auth ("
+                                               + httpSession.getCreationTime() + ")\"");
+                               httpSession.setAttribute(ATTR_AUTH, Boolean.TRUE);
+                               return;
+                       } else {
+                               UsernamePasswordAuthenticationToken token = basicAuth(basicAuth);
+                               Authentication auth = authenticationManager.authenticate(token);
+                               SecurityContextHolder.getContext().setAuthentication(auth);
+                               httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY,
+                                               SecurityContextHolder.getContext());
+                               httpSession.setAttribute(ATTR_AUTH, Boolean.FALSE);
+                       }
+               }
+               // Assume authentication has been done and continue
+               filterChain.doFilter(servletRequest, servletResponse);
+       }
+
+       @Override
+       public void init(FilterConfig filterConfig) throws ServletException {
+       }
+
+       @Override
+       public void destroy() {
+       }
+
+       private UsernamePasswordAuthenticationToken basicAuth(String authHeader) {
+               if (authHeader != null) {
+                       StringTokenizer st = new StringTokenizer(authHeader);
+                       if (st.hasMoreTokens()) {
+                               String basic = st.nextToken();
+                               if (basic.equalsIgnoreCase("Basic")) {
+                                       try {
+                                               String credentials = new String(Base64.decodeBase64(st
+                                                               .nextToken()), "UTF-8");
+                                               log.debug("Credentials: " + credentials);
+                                               int p = credentials.indexOf(":");
+                                               if (p != -1) {
+                                                       String login = credentials.substring(0, p).trim();
+                                                       String password = credentials.substring(p + 1)
+                                                                       .trim();
+
+                                                       return new UsernamePasswordAuthenticationToken(
+                                                                       login, password);
+                                               } else {
+                                                       throw new CmsException(
+                                                                       "Invalid authentication token");
+                                               }
+                                       } catch (Exception e) {
+                                               throw new CmsException(
+                                                               "Couldn't retrieve authentication", e);
+                                       }
+                               }
+                       }
+               }
+               throw new CmsException("Couldn't retrieve authentication");
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeSecurity.java
new file mode 100644 (file)
index 0000000..7c176ea
--- /dev/null
@@ -0,0 +1,91 @@
+package org.argeo.cms.internal.kernel;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.security.UserAdminService;
+import org.argeo.security.core.InternalAuthentication;
+import org.argeo.security.core.InternalAuthenticationProvider;
+import org.argeo.security.jcr.SimpleJcrSecurityModel;
+import org.argeo.security.jcr.jackrabbit.JackrabbitUserAdminService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.springframework.security.authentication.AnonymousAuthenticationProvider;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.provisioning.UserDetailsManager;
+
+/** Authentication and user management. */
+class NodeSecurity implements AuthenticationManager {
+       private final static Log log = LogFactory.getLog(NodeSecurity.class);
+
+       private final BundleContext bundleContext;
+
+       private final InternalAuthenticationProvider internalAuth;
+       private final AnonymousAuthenticationProvider anonymousAuth;
+       private final JackrabbitUserAdminService jackrabbitUserAdmin;
+
+       private ServiceRegistration<AuthenticationManager> authenticationManagerReg;
+       private ServiceRegistration<UserAdminService> userAdminReg;
+       private ServiceRegistration<UserDetailsManager> userDetailsManagerReg;
+
+       public NodeSecurity(BundleContext bundleContext, JackrabbitNode node)
+                       throws RepositoryException {
+               this.bundleContext = bundleContext;
+
+               internalAuth = new InternalAuthenticationProvider(
+                               KernelConstants.DEFAULT_SECURITY_KEY);
+               anonymousAuth = new AnonymousAuthenticationProvider(
+                               KernelConstants.DEFAULT_SECURITY_KEY);
+
+               // user admin
+               jackrabbitUserAdmin = new JackrabbitUserAdminService();
+               jackrabbitUserAdmin.setRepository(node);
+               jackrabbitUserAdmin.setSecurityModel(new SimpleJcrSecurityModel());
+               jackrabbitUserAdmin.init();
+
+       }
+
+       public void publish() {
+               authenticationManagerReg = bundleContext.registerService(
+                               AuthenticationManager.class, this, null);
+               userAdminReg = bundleContext.registerService(UserAdminService.class,
+                               jackrabbitUserAdmin, null);
+               userDetailsManagerReg = bundleContext.registerService(
+                               UserDetailsManager.class, jackrabbitUserAdmin, null);
+               // userAdminReg =
+               // bundleContext.registerService(UserDetailsService.class,
+               // jackrabbitUserAdmin, null);
+       }
+
+       void destroy() {
+               try {
+                       jackrabbitUserAdmin.destroy();
+               } catch (RepositoryException e) {
+                       log.error("Error while destroying Jackrabbit useradmin");
+               }
+               userDetailsManagerReg.unregister();
+               userAdminReg.unregister();
+               authenticationManagerReg.unregister();
+       }
+
+       @Override
+       public Authentication authenticate(Authentication authentication)
+                       throws AuthenticationException {
+               Authentication auth = null;
+               if (authentication instanceof InternalAuthentication)
+                       auth = internalAuth.authenticate(authentication);
+               else if (authentication instanceof AnonymousAuthenticationToken)
+                       auth = anonymousAuth.authenticate(authentication);
+               else if (authentication instanceof UsernamePasswordAuthenticationToken)
+                       auth = jackrabbitUserAdmin.authenticate(authentication);
+               if (auth == null)
+                       throw new CmsException("Could not authenticate " + authentication);
+               return auth;
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jackrabbit-node.properties b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jackrabbit-node.properties
new file mode 100644 (file)
index 0000000..c7ab79d
--- /dev/null
@@ -0,0 +1,16 @@
+# Workspace used by the node session
+argeo.node.repo.defaultWorkspace=main
+#argeo.node.repo.securityWorkspace=security
+argeo.node.repo.forceCndImport=true
+
+# Repository base directory
+argeo.node.repo.home=node
+
+## H2 Embedded (DEFAULT)
+argeo.node.repo.configuration=osgibundle:repository-h2.xml
+argeo.node.repo.dburl=jdbc:h2:${osgi.instance.area}/node/h2/repository
+argeo.node.repo.dbuser=sa
+argeo.node.repo.dbpassword=
+
+# ADVANCED
+argeo.node.repo.maxPoolSize=10
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/repository-h2.xml b/org.argeo.cms/src/org/argeo/cms/internal/kernel/repository-h2.xml
new file mode 100644 (file)
index 0000000..583bf4c
--- /dev/null
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+                            "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+<Repository>
+       <!-- Shared datasource -->
+       <DataSources>
+               <DataSource name="dataSource">
+                       <param name="driver" value="org.h2.Driver" />
+                       <param name="url" value="${argeo.node.repo.dburl}" />
+                       <param name="user" value="${argeo.node.repo.dbuser}" />
+                       <param name="password" value="${argeo.node.repo.dbpassword}" />
+                       <param name="databaseType" value="h2" />
+                       <param name="maxPoolSize" value="${argeo.node.repo.maxPoolSize}" />
+               </DataSource>
+       </DataSources>
+
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+               <param name="dataSourceName" value="dataSource" />
+               <param name="schema" value="default" />
+               <param name="schemaObjectPrefix" value="fs_" />
+       </FileSystem>
+       <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
+               <param name="path" value="${rep.home}/datastore" />
+       </DataStore>
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="${argeo.node.repo.defaultWorkspace}" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="default" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+                       <param name="initializeHierarchyCache" value="true" />
+               </SearchIndex>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="default" />
+                       <param name="schemaObjectPrefix" value="fs_ver_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="pm_ver_" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="initializeHierarchyCache" value="true" />
+               <!-- <param name="extractorPoolSize" value="2" /> -->
+               <!-- <param name="supportHighlighting" value="true" /> -->
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/repository-memory.xml b/org.argeo.cms/src/org/argeo/cms/internal/kernel/repository-memory.xml
new file mode 100644 (file)
index 0000000..b41cfad
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+                            "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+<Repository>
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="${argeo.node.repo.defaultWorkspace}" configRootPath="/workspaces" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="blobFSBlockSize" value="1" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+                       <param name="directoryManagerClass"
+                               value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+                       <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               </SearchIndex>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+                       <param name="blobFSBlockSize" value="1" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="directoryManagerClass"
+                       value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+               <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/repository-postgresql.xml b/org.argeo.cms/src/org/argeo/cms/internal/kernel/repository-postgresql.xml
new file mode 100644 (file)
index 0000000..811f0c6
--- /dev/null
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+                            "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+<Repository>
+       <!-- Shared datasource -->
+       <DataSources>
+               <DataSource name="dataSource">
+                       <param name="driver" value="org.postgresql.Driver" />
+                       <param name="url" value="${argeo.node.repo.dburl}" />
+                       <param name="user" value="${argeo.node.repo.dbuser}" />
+                       <param name="password" value="${argeo.node.repo.dbpassword}" />
+                       <param name="databaseType" value="postgresql" />
+                       <param name="maxPoolSize" value="${argeo.node.repo.maxPoolSize}" />
+               </DataSource>
+       </DataSources>
+
+       <!-- File system and datastore -->
+       <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+               <param name="dataSourceName" value="dataSource" />
+               <param name="schema" value="postgresql" />
+               <param name="schemaObjectPrefix" value="fs_" />
+       </FileSystem>
+       <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
+               <param name="path" value="${rep.home}/datastore" />
+       </DataStore>
+
+       <!-- Workspace templates -->
+       <Workspaces rootPath="${rep.home}/workspaces"
+               defaultWorkspace="${argeo.node.repo.defaultWorkspace}" />
+       <Workspace name="${wsp.name}">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="postgresql" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
+               </PersistenceManager>
+               <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+                       <param name="path" value="${wsp.home}/index" />
+               </SearchIndex>
+       </Workspace>
+
+       <!-- Versioning -->
+       <Versioning rootPath="${rep.home}/version">
+               <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schema" value="postgresql" />
+                       <param name="schemaObjectPrefix" value="fs_ver_" />
+               </FileSystem>
+               <PersistenceManager
+                       class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
+                       <param name="dataSourceName" value="dataSource" />
+                       <param name="schemaObjectPrefix" value="pm_ver_" />
+               </PersistenceManager>
+       </Versioning>
+
+       <!-- Indexing -->
+       <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+               <param name="path" value="${rep.home}/index" />
+               <param name="extractorPoolSize" value="2" />
+               <param name="supportHighlighting" value="true" />
+       </SearchIndex>
+
+       <!-- Security -->
+       <Security appName="Jackrabbit">
+               <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+                       workspaceName="security">
+               </SecurityManager>
+               <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager">
+               </AccessManager>
+               <LoginModule class="org.argeo.security.jackrabbit.ArgeoLoginModule">
+               </LoginModule>
+       </Security>
+</Repository>
\ No newline at end of file