]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java
Big cleanup of the security layers
[lgpl/argeo-commons.git] / server / runtime / org.argeo.server.jackrabbit / src / main / java / org / argeo / jackrabbit / JackrabbitContainer.java
index a708aadd1d67a6eada7401b8bcf94d3cf95ff8ad..10c7af1d05e002359ce4abf758b3ebb7d9c08fa0 100644 (file)
 
 package org.argeo.jackrabbit;
 
-import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -40,6 +40,7 @@ import javax.jcr.Node;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
 import javax.jcr.Value;
 
 import org.apache.commons.io.FileUtils;
@@ -50,16 +51,17 @@ import org.apache.jackrabbit.api.JackrabbitRepository;
 import org.apache.jackrabbit.commons.NamespaceHelper;
 import org.apache.jackrabbit.commons.cnd.CndImporter;
 import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.TransientRepository;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
 import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
 import org.apache.jackrabbit.jcr2dav.Jcr2davRepositoryFactory;
 import org.argeo.ArgeoException;
 import org.argeo.jcr.ArgeoNames;
 import org.argeo.jcr.JcrUtils;
-import org.springframework.context.ResourceLoaderAware;
+import org.argeo.security.SystemAuthentication;
 import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
+import org.springframework.security.Authentication;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.util.SystemPropertyUtils;
 import org.xml.sax.InputSource;
 
@@ -67,23 +69,24 @@ import org.xml.sax.InputSource;
  * Wrapper around a Jackrabbit repository which allows to configure it in Spring
  * and expose it as a {@link Repository}.
  */
-public class JackrabbitContainer implements Repository, ResourceLoaderAware {
+public class JackrabbitContainer implements Repository {
        private Log log = LogFactory.getLog(JackrabbitContainer.class);
 
+       // remote
+       private String uri = null;
+       private Credentials remoteSystemCredentials = null;
+
+       // local
        private Resource configuration;
+       private RepositoryConfig repositoryConfig;
        private File homeDirectory;
        private Resource variables;
-
        private Boolean inMemory = false;
-       private String uri = null;
 
        // wrapped repository
        private Repository repository;
-       private RepositoryConfig repositoryConfig;
-
-       // CND
-       private ResourceLoader resourceLoader;
 
+       // data model
        /** Node type definitions in CND format */
        private List<String> cndFiles = new ArrayList<String>();
 
@@ -97,29 +100,46 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
 
        private Executor systemExecutor;
 
-       public void init() throws Exception {
+       /**
+        * Empty constructor, {@link #init()} should be called after properties have
+        * been set
+        */
+       public JackrabbitContainer() {
+       }
+
+       /**
+        * Convenience constructor for remote, {@link #init()} is called in the
+        * constructor.
+        */
+       public JackrabbitContainer(String uri, Credentials remoteSystemCredentials) {
+               setUri(uri);
+               setRemoteSystemCredentials(remoteSystemCredentials);
+               init();
+       }
+
+       /** Initializes */
+       public void init() {
                if (repository != null) {
                        // we are just wrapping another repository
-                       importNodeTypeDefinitions(repository);
+                       prepareDataModel();
                        return;
                }
 
                createJackrabbitRepository();
-
                // migrate if needed
                migrate();
 
                // apply new CND files after migration
                if (cndFiles != null && cndFiles.size() > 0)
-                       importNodeTypeDefinitions(repository);
+                       prepareDataModel();
        }
 
-       /** Actually creates a new repository. */
+       /** Actually creates the new repository. */
        protected void createJackrabbitRepository() {
                long begin = System.currentTimeMillis();
+               InputStream configurationIn = null;
                try {
-                       // remote repository
-                       if (uri != null && !uri.trim().equals("")) {
+                       if (uri != null && !uri.trim().equals("")) {// remote
                                Map<String, String> params = new HashMap<String, String>();
                                params.put(
                                                org.apache.jackrabbit.commons.JcrUtils.REPOSITORY_URI,
@@ -131,50 +151,52 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                                                        + " not found");
                                log.info("Initialized Jackrabbit repository " + repository
                                                + " from URI " + uri);
-                               // do not perform further initialization since we assume that
-                               // the
-                               // remote repository has been properly configured
-                               return;
-                       }
-
-                       // local repository
-                       if (inMemory && getHomeDirectory().exists()) {
-                               FileUtils.deleteDirectory(getHomeDirectory());
-                               log.warn("Deleted Jackrabbit home directory "
-                                               + getHomeDirectory());
-                       }
+                               // we assume that the remote repository has been properly
+                               // configured
+                       } else {// local
+                               // reset uri to null in order to optimize isRemote()
+                               uri = null;
+
+                               // temporary
+                               if (inMemory && getHomeDirectory().exists()) {
+                                       FileUtils.deleteDirectory(getHomeDirectory());
+                                       log.warn("Deleted Jackrabbit home directory "
+                                                       + getHomeDirectory());
+                               }
 
-                       Properties vars = getConfigurationProperties();
-                       InputStream in = configuration.getInputStream();
-                       try {
+                               // process configuration file
+                               Properties vars = getConfigurationProperties();
+                               configurationIn = configuration.getInputStream();
                                vars.put(
                                                RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
                                                getHomeDirectory().getCanonicalPath());
-                               repositoryConfig = RepositoryConfig.create(new InputSource(in),
-                                               vars);
-                       } catch (Exception e) {
-                               throw new RuntimeException("Cannot read configuration", e);
-                       } finally {
-                               IOUtils.closeQuietly(in);
-                       }
+                               repositoryConfig = RepositoryConfig.create(new InputSource(
+                                               configurationIn), vars);
 
-                       if (inMemory)
-                               repository = new TransientRepository(repositoryConfig);
-                       else
+                               //
+                               // Actual repository creation
+                               //
                                repository = RepositoryImpl.create(repositoryConfig);
 
-                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-                       log.info("Initialized Jackrabbit repository in " + duration
-                                       + " s, home: " + getHomeDirectory() + ", config: "
-                                       + configuration);
+                               double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                               log.info("Initialized Jackrabbit repository in " + duration
+                                               + " s, home: " + getHomeDirectory() + ", config: "
+                                               + configuration);
+                       }
                } catch (Exception e) {
                        throw new ArgeoException("Cannot create Jackrabbit repository "
                                        + getHomeDirectory(), e);
+               } finally {
+                       IOUtils.closeQuietly(configurationIn);
                }
        }
 
        /** Executes migrations, if needed. */
        protected void migrate() {
+               // Remote migration not supported
+               if (isRemote())
+                       return;
+
                // No migration to perform
                if (dataModelMigrations.size() == 0)
                        return;
@@ -246,7 +268,7 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                                                                        + File.separator + "jackrabbit-"
                                                                        + UUID.randomUUID());
                                        homeDirectory.mkdirs();
-                                       // will it work if directory is not empty?
+                                       // will it work if directory is not empty??
                                        homeDirectory.deleteOnExit();
                                }
                        }
@@ -258,47 +280,39 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                }
        }
 
-       public void dispose() throws Exception {
-               long begin = System.currentTimeMillis();
-               if (repository != null) {
-                       if (repository instanceof JackrabbitRepository)
-                               ((JackrabbitRepository) repository).shutdown();
-                       else if (repository instanceof RepositoryImpl)
-                               ((RepositoryImpl) repository).shutdown();
-                       else if (repository instanceof TransientRepository)
-                               ((TransientRepository) repository).shutdown();
-               }
-
-               if (inMemory)
-                       if (getHomeDirectory().exists()) {
-                               FileUtils.deleteDirectory(getHomeDirectory());
-                               if (log.isDebugEnabled())
-                                       log.debug("Deleted Jackrabbit home directory "
-                                                       + getHomeDirectory());
-                       }
-
-               double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-               if (uri != null && !uri.trim().equals(""))
-                       log.info("Destroyed Jackrabbit repository with uri " + uri);
-               else
+       /** Shutdown the repository */
+       public void destroy() throws Exception {
+               if (repository != null && repository instanceof RepositoryImpl) {
+                       long begin = System.currentTimeMillis();
+                       ((RepositoryImpl) repository).shutdown();
+                       if (inMemory)
+                               if (getHomeDirectory().exists()) {
+                                       FileUtils.deleteDirectory(getHomeDirectory());
+                                       if (log.isDebugEnabled())
+                                               log.debug("Deleted Jackrabbit home directory "
+                                                               + getHomeDirectory());
+                               }
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
                        log.info("Destroyed Jackrabbit repository in " + duration
                                        + " s, home: " + getHomeDirectory() + ", config "
                                        + configuration);
+               }
        }
 
        /**
-        * @deprecated explicitly declare {@link #dispose()} as destroy-method
+        * @deprecated explicitly declare {@link #destroy()} as destroy-method
         *             instead.
         */
-       public void destroy() throws Exception {
-               log.error("## Declare destroy-method=\"dispose\". in the Jackrabbit container bean");
+       public void dispose() throws Exception {
+               log.error("## Declare destroy-method=\"destroy\". in the Jackrabbit container bean");
+               destroy();
        }
 
-       /** @deprecated explicitly declare {@link #init()} as init-method instead. */
-       public void afterPropertiesSet() throws Exception {
-               log.error("## Declare init-method=\"init\". in the Jackrabbit container bean");
-       }
+       /*
+        * UTILITIES
+        */
 
+       /** Generates the properties to use in the configuration. */
        protected Properties getConfigurationProperties() {
                InputStream propsIn = null;
                Properties vars;
@@ -333,34 +347,56 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
        }
 
        /**
-        * Import declared node type definitions, trying to update them if they have
-        * changed. In case of failures an error will be logged but no exception
-        * will be thrown.
+        * Import declared node type definitions and register namespaces. Tries to
+        * update the node definitions if they have changed. In case of failures an
+        * error will be logged but no exception will be thrown.
         */
-       protected void importNodeTypeDefinitions(final Repository repository) {
+       protected void prepareDataModel() {
+               // importing node def on remote si currently not supported
+               if (isRemote())
+                       return;
+
                Runnable action = new Runnable() {
                        public void run() {
-                               Reader reader = null;
                                Session session = null;
                                try {
-                                       session = repository.login();
-                                       processNewSession(session);
-                                       // Load cnds as resources
+                                       session = login();
+                                       // register namespaces
+                                       if (namespaces.size() > 0) {
+                                               NamespaceHelper namespaceHelper = new NamespaceHelper(
+                                                               session);
+                                               namespaceHelper.registerNamespaces(namespaces);
+                                       }
+                                       // load CND files from classpath or as URL
                                        for (String resUrl : cndFiles) {
-                                               Resource res = resourceLoader.getResource(resUrl);
-                                               byte[] arr = IOUtils.toByteArray(res.getInputStream());
-                                               reader = new InputStreamReader(
-                                                               new ByteArrayInputStream(arr));
-                                               CndImporter.registerNodeTypes(reader, session, true);
+                                               boolean classpath;
+                                               if (resUrl.startsWith("classpath:")) {
+                                                       resUrl = resUrl.substring("classpath:".length());
+                                                       classpath = true;
+                                               } else if (resUrl.indexOf(':') < 0) {
+                                                       classpath = true;
+                                               } else {
+                                                       classpath = false;
+                                               }
+
+                                               URL url = classpath ? getClass().getClassLoader()
+                                                               .getResource(resUrl) : new URL(resUrl);
+
+                                               Reader reader = null;
+                                               try {
+                                                       reader = new InputStreamReader(url.openStream());
+                                                       CndImporter
+                                                                       .registerNodeTypes(reader, session, true);
+                                               } finally {
+                                                       IOUtils.closeQuietly(reader);
+                                               }
                                        }
-                                       session.save();
                                } catch (Exception e) {
                                        log.error(
                                                        "Cannot import node type definitions " + cndFiles,
                                                        e);
                                        JcrUtils.discardQuietly(session);
                                } finally {
-                                       IOUtils.closeQuietly(reader);
                                        JcrUtils.logoutQuietly(session);
                                }
                        }
@@ -372,7 +408,10 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                        action.run();
        }
 
-       // JCR REPOSITORY (delegated)
+       /*
+        * DELEGATED JCR REPOSITORY METHODS
+        */
+
        public String getDescriptor(String key) {
                return getRepository().getDescriptor(key);
        }
@@ -381,20 +420,32 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return getRepository().getDescriptorKeys();
        }
 
-       public Session login() throws LoginException, RepositoryException {
-               Session session = getRepository().login();
-               processNewSession(session);
-               return session;
-       }
-
+       /** Central login method */
        public Session login(Credentials credentials, String workspaceName)
                        throws LoginException, NoSuchWorkspaceException,
                        RepositoryException {
+
+               // retrieve credentials for remote
+               if (credentials == null && isRemote()) {
+                       Authentication authentication = SecurityContextHolder.getContext()
+                                       .getAuthentication();
+                       if (authentication != null) {
+                               if (authentication instanceof UsernamePasswordAuthenticationToken) {
+                                       UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;
+                                       credentials = new SimpleCredentials(upat.getName(), upat
+                                                       .getCredentials().toString().toCharArray());
+                               } else if ((authentication instanceof SystemAuthentication)
+                                               && remoteSystemCredentials != null) {
+                                       credentials = remoteSystemCredentials;
+                               }
+                       }
+               }
+
                Session session;
                try {
                        session = getRepository().login(credentials, workspaceName);
                } catch (NoSuchWorkspaceException e) {
-                       if (autocreateWorkspaces)
+                       if (autocreateWorkspaces && workspaceName != null)
                                session = createWorkspaceAndLogsIn(credentials, workspaceName);
                        else
                                throw e;
@@ -403,26 +454,26 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return session;
        }
 
+       public Session login() throws LoginException, RepositoryException {
+               return login(null, null);
+       }
+
        public Session login(Credentials credentials) throws LoginException,
                        RepositoryException {
-               Session session = getRepository().login(credentials);
-               processNewSession(session);
-               return session;
+               return login(credentials, null);
        }
 
        public Session login(String workspaceName) throws LoginException,
                        NoSuchWorkspaceException, RepositoryException {
-               Session session;
-               try {
-                       session = getRepository().login(workspaceName);
-               } catch (NoSuchWorkspaceException e) {
-                       if (autocreateWorkspaces)
-                               session = createWorkspaceAndLogsIn(null, workspaceName);
-                       else
-                               throw e;
-               }
-               processNewSession(session);
-               return session;
+               return login(null, workspaceName);
+       }
+
+       /** Called after a session has been created, does nothing by default. */
+       protected void processNewSession(Session session) {
+       }
+
+       public Boolean isRemote() {
+               return uri != null;
        }
 
        /** Wraps access to the repository, making sure it is available. */
@@ -436,15 +487,6 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return repository;
        }
 
-       protected synchronized void processNewSession(Session session) {
-               try {
-                       NamespaceHelper namespaceHelper = new NamespaceHelper(session);
-                       namespaceHelper.registerNamespaces(namespaces);
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot process new session", e);
-               }
-       }
-
        /**
         * Logs in to the default workspace, creates the required workspace, logs
         * out, logs in to the required workspace.
@@ -459,10 +501,6 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return getRepository().login(credentials, workspaceName);
        }
 
-       public void setResourceLoader(ResourceLoader resourceLoader) {
-               this.resourceLoader = resourceLoader;
-       }
-
        public boolean isStandardDescriptor(String key) {
                return getRepository().isStandardDescriptor(key);
        }
@@ -479,7 +517,10 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return getRepository().getDescriptorValues(key);
        }
 
-       // BEANS METHODS
+       /*
+        * FIELDS ACCESS
+        */
+
        public void setHomeDirectory(File homeDirectory) {
                this.homeDirectory = homeDirectory;
        }
@@ -508,6 +549,10 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                this.uri = uri;
        }
 
+       public void setRemoteSystemCredentials(Credentials remoteSystemCredentials) {
+               this.remoteSystemCredentials = remoteSystemCredentials;
+       }
+
        public void setSystemExecutor(Executor systemExecutor) {
                this.systemExecutor = systemExecutor;
        }
@@ -520,5 +565,4 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                        Set<JackrabbitDataModelMigration> dataModelMigrations) {
                this.dataModelMigrations = dataModelMigrations;
        }
-
 }