Introduce support for multiple JCR repositories.
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 6 Feb 2018 12:21:38 +0000 (13:21 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 6 Feb 2018 12:21:38 +0000 (13:21 +0100)
Framework properties override deploy config by clean (and not only by
first init).

14 files changed:
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/DataModels.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/kernel/InitUtils.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/LocalRepository.java
org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java
org.argeo.enterprise/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
org.argeo.enterprise/src/org/argeo/osgi/useradmin/DigestUtils.java
org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserAdminConf.java
org.argeo.enterprise/src/org/argeo/osgi/useradmin/UserDirectory.java
org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java
org.argeo.node.api/src/org/argeo/node/NodeConstants.java

index e25ff7747365871d7a289f599ad38af5396bc0b1..0a04e0804b2529d0611eff38f723af579a648ef3 100644 (file)
@@ -45,11 +45,13 @@ import org.osgi.service.useradmin.UserAdmin;
 import org.osgi.util.tracker.ServiceTracker;
 
 public class CmsDeployment implements NodeDeployment {
-       private final static String LEGACY_JCR_REPOSITORY_ALIAS = "argeo.jcr.repository.alias";
+       // private final static String LEGACY_JCR_REPOSITORY_ALIAS =
+       // "argeo.jcr.repository.alias";
 
        private final Log log = LogFactory.getLog(getClass());
        private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
 
+       private DataModels dataModels;
        private DeployConfig deployConfig;
        private HomeRepository homeRepository;
 
@@ -74,6 +76,7 @@ public class CmsDeployment implements NodeDeployment {
                cleanState = nodeState.isClean();
 
                nodeHttp = new NodeHttp();
+               dataModels = new DataModels(bc);
                initTrackers();
        }
 
@@ -110,7 +113,7 @@ public class CmsDeployment implements NodeDeployment {
                        @Override
                        public ConfigurationAdmin addingService(ServiceReference<ConfigurationAdmin> reference) {
                                ConfigurationAdmin configurationAdmin = bc.getService(reference);
-                               deployConfig = new DeployConfig(configurationAdmin, cleanState);
+                               deployConfig = new DeployConfig(configurationAdmin, dataModels, cleanState);
                                httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null;
                                try {
                                        // Configuration[] configs = configurationAdmin
@@ -205,13 +208,13 @@ public class CmsDeployment implements NodeDeployment {
                }
 
                // home
-               prepareDataModel(KernelUtils.openAdminSession(deployedNodeRepository));
+               prepareDataModel(NodeConstants.NODE, KernelUtils.openAdminSession(deployedNodeRepository));
        }
 
        private void prepareHomeRepository(Repository deployedRepository) {
                Hashtable<String, String> regProps = new Hashtable<String, String>();
                regProps.put(NodeConstants.CN, NodeConstants.HOME);
-               regProps.put(LEGACY_JCR_REPOSITORY_ALIAS, NodeConstants.HOME);
+               // regProps.put(LEGACY_JCR_REPOSITORY_ALIAS, NodeConstants.HOME);
                homeRepository = new HomeRepository(deployedRepository);
                // register
                bc.registerService(Repository.class, homeRepository, regProps);
@@ -232,36 +235,45 @@ public class CmsDeployment implements NodeDeployment {
        }
 
        /** Session is logged out. */
-       private void prepareDataModel(Session adminSession) {
+       private void prepareDataModel(String cn, Session adminSession) {
                try {
                        Set<String> processed = new HashSet<String>();
                        bundles: for (Bundle bundle : bc.getBundles()) {
                                BundleWiring wiring = bundle.adapt(BundleWiring.class);
                                if (wiring == null)
                                        continue bundles;
-                               processWiring(adminSession, wiring, processed);
+                               if (NodeConstants.NODE.equals(cn))// process all data models
+                                       processWiring(cn, adminSession, wiring, processed);
+                               else {
+                                       List<BundleCapability> capabilities = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
+                                       for (BundleCapability capability : capabilities) {
+                                               String dataModelName = (String) capability.getAttributes().get(DataModelNamespace.NAME);
+                                               if (dataModelName.equals(cn))// process only own data model
+                                                       processWiring(cn, adminSession, wiring, processed);
+                                       }
+                               }
                        }
                } finally {
                        JcrUtils.logoutQuietly(adminSession);
                }
        }
 
-       private void processWiring(Session adminSession, BundleWiring wiring, Set<String> processed) {
+       private void processWiring(String cn, Session adminSession, BundleWiring wiring, Set<String> processed) {
                // recursively process requirements first
                List<BundleWire> requiredWires = wiring.getRequiredWires(CMS_DATA_MODEL_NAMESPACE);
                for (BundleWire wire : requiredWires) {
-                       processWiring(adminSession, wire.getProviderWiring(), processed);
-                       // registerCnd(adminSession, wire.getCapability(), processed);
+                       processWiring(cn, adminSession, wire.getProviderWiring(), processed);
                }
                List<BundleCapability> capabilities = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
                for (BundleCapability capability : capabilities) {
-                       registerDataModelCapability(adminSession, capability, processed);
+                       registerDataModelCapability(cn, adminSession, capability, processed);
                }
        }
 
-       private void registerDataModelCapability(Session adminSession, BundleCapability capability, Set<String> processed) {
+       private void registerDataModelCapability(String cn, Session adminSession, BundleCapability capability,
+                       Set<String> processed) {
                Map<String, Object> attrs = capability.getAttributes();
-               String name = (String) attrs.get(DataModelNamespace.CAPABILITY_NAME_ATTRIBUTE);
+               String name = (String) attrs.get(DataModelNamespace.NAME);
                if (processed.contains(name)) {
                        if (log.isTraceEnabled())
                                log.trace("Data model " + name + " has already been processed");
@@ -269,7 +281,7 @@ public class CmsDeployment implements NodeDeployment {
                }
 
                // CND
-               String path = (String) attrs.get(DataModelNamespace.CAPABILITY_CND_ATTRIBUTE);
+               String path = (String) attrs.get(DataModelNamespace.CND);
                if (path != null) {
                        File dataModel = bc.getBundle().getDataFile("dataModels/" + path);
                        if (!dataModel.exists()) {
@@ -289,9 +301,21 @@ public class CmsDeployment implements NodeDeployment {
                        }
                }
 
-               if (!asBoolean((String) attrs.get(DataModelNamespace.CAPABILITY_ABSTRACT_ATTRIBUTE))) {
+               if (KernelUtils.asBoolean((String) attrs.get(DataModelNamespace.ABSTRACT)))
+                       return;
+               // Non abstract
+               boolean isStandalone = deployConfig.isStandalone(name);
+               boolean publishLocalRepo;
+               if (isStandalone && name.equals(cn))// includes the node itself
+                       publishLocalRepo = true;
+               else if (!isStandalone && cn.equals(NodeConstants.NODE))
+                       publishLocalRepo = true;
+               else
+                       publishLocalRepo = false;
+
+               if (publishLocalRepo) {
                        Hashtable<String, Object> properties = new Hashtable<>();
-                       properties.put(LEGACY_JCR_REPOSITORY_ALIAS, name);
+                       // properties.put(LEGACY_JCR_REPOSITORY_ALIAS, name);
                        properties.put(NodeConstants.CN, name);
                        if (name.equals(NodeConstants.NODE))
                                properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
@@ -302,20 +326,6 @@ public class CmsDeployment implements NodeDeployment {
                }
        }
 
-       private boolean asBoolean(String value) {
-               if (value == null)
-                       return false;
-               switch (value) {
-               case "true":
-                       return true;
-               case "false":
-                       return false;
-               default:
-                       throw new CmsException("Unsupported value for attribute " + DataModelNamespace.CAPABILITY_ABSTRACT_ATTRIBUTE
-                                       + ": " + value);
-               }
-       }
-
        @Override
        public Long getAvailableSince() {
                return availableSince;
@@ -329,19 +339,20 @@ public class CmsDeployment implements NodeDeployment {
 
                @Override
                public RepositoryContext addingService(ServiceReference<RepositoryContext> reference) {
-                       RepositoryContext nodeRepo = bc.getService(reference);
-                       Object cn = reference.getProperty(NodeConstants.CN);
+                       RepositoryContext repoContext = bc.getService(reference);
+                       String cn = (String) reference.getProperty(NodeConstants.CN);
                        if (cn != null) {
                                if (cn.equals(NodeConstants.NODE)) {
-                                       prepareNodeRepository(nodeRepo.getRepository());
-                                       prepareHomeRepository(nodeRepo.getRepository());
+                                       prepareNodeRepository(repoContext.getRepository());
+                                       // TODO separate home repository
+                                       prepareHomeRepository(repoContext.getRepository());
                                        nodeAvailable = true;
                                        checkReadiness();
                                } else {
-                                       // TODO standalone
+                                       prepareDataModel(cn, KernelUtils.openAdminSession(repoContext.getRepository()));
                                }
                        }
-                       return nodeRepo;
+                       return repoContext;
                }
 
                @Override
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataModels.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataModels.java
new file mode 100644 (file)
index 0000000..b758a30
--- /dev/null
@@ -0,0 +1,163 @@
+package org.argeo.cms.internal.kernel;
+
+import static org.argeo.node.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.argeo.cms.CmsException;
+import org.argeo.node.DataModelNamespace;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+class DataModels implements BundleListener {
+       private Map<String, DataModel> dataModels = new TreeMap<>();
+
+       public DataModels(BundleContext bc) {
+               for (Bundle bundle : bc.getBundles())
+                       processBundle(bundle);
+               bc.addBundleListener(this);
+       }
+
+       public List<DataModel> getNonAbstractDataModels() {
+               List<DataModel> res = new ArrayList<>();
+               for (String name : dataModels.keySet()) {
+                       DataModel dataModel = dataModels.get(name);
+                       if (!dataModel.isAbstract())
+                               res.add(dataModel);
+               }
+               // TODO reorder?
+               return res;
+       }
+
+       @Override
+       public void bundleChanged(BundleEvent event) {
+               if (event.getType() == Bundle.RESOLVED) {
+                       processBundle(event.getBundle());
+               } else if (event.getType() == Bundle.UNINSTALLED) {
+                       BundleWiring wiring = event.getBundle().adapt(BundleWiring.class);
+                       List<BundleCapability> providedDataModels = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
+                       if (providedDataModels.size() == 0)
+                               return;
+                       for (BundleCapability bundleCapability : providedDataModels) {
+                               dataModels.remove(bundleCapability.getAttributes().get(DataModelNamespace.NAME));
+                       }
+               }
+
+       }
+
+       protected void processBundle(Bundle bundle) {
+               BundleWiring wiring = bundle.adapt(BundleWiring.class);
+               List<BundleCapability> providedDataModels = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
+               if (providedDataModels.size() == 0)
+                       return;
+               List<BundleWire> requiredDataModels = wiring.getRequiredWires(CMS_DATA_MODEL_NAMESPACE);
+               for (BundleCapability bundleCapability : providedDataModels) {
+                       DataModel dataModel = new DataModel(bundleCapability, requiredDataModels);
+                       dataModels.put(dataModel.getName(), dataModel);
+               }
+       }
+
+       /** Return a negative depth if dataModel is required by ref, 0 otherwise. */
+       static int required(DataModel ref, DataModel dataModel, int depth) {
+               for (DataModel dm : ref.getRequired()) {
+                       if (dm.equals(dataModel))// found here
+                               return depth - 1;
+                       int d = required(dm, dataModel, depth - 1);
+                       if (d != 0)// found deeper
+                               return d;
+               }
+               return 0;// not found
+       }
+
+       class DataModel {
+               private final String name;
+               private final boolean abstrct;
+               // private final boolean standalone;
+               private final String cnd;
+               private final List<DataModel> required;
+
+               private DataModel(BundleCapability bundleCapability, List<BundleWire> requiredDataModels) {
+                       assert CMS_DATA_MODEL_NAMESPACE.equals(bundleCapability.getNamespace());
+                       Map<String, Object> attrs = bundleCapability.getAttributes();
+                       name = (String) attrs.get(DataModelNamespace.NAME);
+                       assert name != null;
+                       abstrct = KernelUtils.asBoolean((String) attrs.get(DataModelNamespace.ABSTRACT));
+                       // standalone = KernelUtils.asBoolean((String)
+                       // attrs.get(DataModelNamespace.CAPABILITY_STANDALONE_ATTRIBUTE));
+                       cnd = (String) attrs.get(DataModelNamespace.CND);
+                       List<DataModel> req = new ArrayList<>();
+                       for (BundleWire wire : requiredDataModels) {
+                               String requiredDataModelName = (String) wire.getCapability().getAttributes()
+                                               .get(DataModelNamespace.NAME);
+                               assert requiredDataModelName != null;
+                               DataModel requiredDataModel = dataModels.get(requiredDataModelName);
+                               if (requiredDataModel == null)
+                                       throw new CmsException("No required data model " + requiredDataModelName);
+                               req.add(requiredDataModel);
+                       }
+                       required = Collections.unmodifiableList(req);
+               }
+
+               public String getName() {
+                       return name;
+               }
+
+               public boolean isAbstract() {
+                       return abstrct;
+               }
+
+               // public boolean isStandalone() {
+               // return !isAbstract();
+               // }
+
+               public String getCnd() {
+                       return cnd;
+               }
+
+               public List<DataModel> getRequired() {
+                       return required;
+               }
+
+               // @Override
+               // public int compareTo(DataModel o) {
+               // if (equals(o))
+               // return 0;
+               // int res = required(this, o, 0);
+               // if (res != 0)
+               // return res;
+               // // the other way round
+               // res = required(o, this, 0);
+               // if (res != 0)
+               // return -res;
+               // return 0;
+               // }
+
+               @Override
+               public int hashCode() {
+                       return name.hashCode();
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       if (obj instanceof DataModel)
+                               return ((DataModel) obj).name.equals(name);
+                       return false;
+               }
+
+               @Override
+               public String toString() {
+                       return "Data model " + name;
+               }
+
+       }
+
+}
index a2618922212b3ec34ebfba678f2c972ffb7f1042..bff544ab23dbba6fc5bcdfcf4649d81d33237a41 100644 (file)
@@ -5,6 +5,7 @@ import java.io.InputStream;
 import java.io.Writer;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.List;
 import java.util.SortedMap;
@@ -23,6 +24,7 @@ import org.argeo.naming.AttributesDictionary;
 import org.argeo.naming.LdifParser;
 import org.argeo.naming.LdifWriter;
 import org.argeo.node.NodeConstants;
+import org.argeo.osgi.useradmin.UserAdminConf;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.service.cm.Configuration;
@@ -36,15 +38,19 @@ class DeployConfig implements ConfigurationListener {
 
        private static Path deployConfigPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEPLOY_CONFIG_PATH);
        private SortedMap<LdapName, Attributes> deployConfigs = new TreeMap<>();
+       private final DataModels dataModels;
 
-       public DeployConfig(ConfigurationAdmin configurationAdmin, boolean isClean) {
+       public DeployConfig(ConfigurationAdmin configurationAdmin, DataModels dataModels, boolean isClean) {
+               this.dataModels = dataModels;
                // ConfigurationAdmin configurationAdmin =
                // bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
                try {
+                       boolean isFirstInit = false;
                        if (!isInitialized()) { // first init
+                               isFirstInit = true;
                                firstInit();
                        }
-                       init(configurationAdmin, isClean);
+                       init(configurationAdmin, isClean, isFirstInit);
                } catch (IOException e) {
                        throw new CmsException("Could not init deploy configs", e);
                }
@@ -53,10 +59,11 @@ class DeployConfig implements ConfigurationListener {
        }
 
        private void firstInit() throws IOException {
+               log.info("## FIRST INIT ##");
                Files.createDirectories(deployConfigPath.getParent());
 
-               FirstInit firstInit = new FirstInit();
-               FirstInit.prepareInstanceArea();
+               // FirstInit firstInit = new FirstInit();
+               InitUtils.prepareFirstInitInstanceArea();
 
                if (!Files.exists(deployConfigPath))
                        deployConfigs = new TreeMap<>();
@@ -64,25 +71,56 @@ class DeployConfig implements ConfigurationListener {
                        try (InputStream in = Files.newInputStream(deployConfigPath)) {
                                deployConfigs = new LdifParser().read(in);
                        }
+               save();
+       }
 
+       private void setFromFrameworkProperties(boolean isFirstInit) {
                // node repository
-               Dictionary<String, Object> nodeConfig = firstInit
+               Dictionary<String, Object> nodeConfig = InitUtils
                                .getNodeRepositoryConfig(getProps(NodeConstants.NODE_REPOS_FACTORY_PID, NodeConstants.NODE));
                // node repository is mandatory
                putFactoryDeployConfig(NodeConstants.NODE_REPOS_FACTORY_PID, nodeConfig);
 
-               // user admin
+               // additional repositories
+               dataModels: for (DataModels.DataModel dataModel : dataModels.getNonAbstractDataModels()) {
+                       if (NodeConstants.NODE.equals(dataModel.getName()))
+                               continue dataModels;
+                       Dictionary<String, Object> config = InitUtils.getRepositoryConfig(dataModel.getName(),
+                                       getProps(NodeConstants.NODE_REPOS_FACTORY_PID, NodeConstants.NODE));
+                       if (config.size() != 0)
+                               putFactoryDeployConfig(NodeConstants.NODE_REPOS_FACTORY_PID, config);
+               }
 
-               List<Dictionary<String, Object>> userDirectoryConfigs = firstInit.getUserDirectoryConfigs();
-               for (int i = 0; i < userDirectoryConfigs.size(); i++) {
-                       Dictionary<String, Object> userDirectoryConfig = userDirectoryConfigs.get(i);
-                       String cn = Integer.toString(i);
-                       userDirectoryConfig.put(NodeConstants.CN, cn);
-                       putFactoryDeployConfig(NodeConstants.NODE_USER_ADMIN_PID, userDirectoryConfig);
+               // user admin
+               List<Dictionary<String, Object>> userDirectoryConfigs = InitUtils.getUserDirectoryConfigs();
+               if (userDirectoryConfigs.size() != 0) {
+                       List<String> activeCns = new ArrayList<>();
+                       for (int i = 0; i < userDirectoryConfigs.size(); i++) {
+                               Dictionary<String, Object> userDirectoryConfig = userDirectoryConfigs.get(i);
+                               String cn = UserAdminConf.baseDnHash(userDirectoryConfig);
+                               activeCns.add(cn);
+                               userDirectoryConfig.put(NodeConstants.CN, cn);
+                               putFactoryDeployConfig(NodeConstants.NODE_USER_ADMIN_PID, userDirectoryConfig);
+                       }
+                       // disable others
+                       LdapName userAdminFactoryName = serviceFactoryDn(NodeConstants.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(UserAdminConf.disabled.name(), "true");
+                                               }
+                                       } catch (Exception e) {
+                                               throw new CmsException("Cannot disable user directory " + name, e);
+                                       }
+                               }
+                       }
                }
 
                // http server
-               Dictionary<String, Object> webServerConfig = firstInit
+               Dictionary<String, Object> webServerConfig = InitUtils
                                .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, NodeConstants.DEFAULT));
                if (!webServerConfig.isEmpty())
                        putFactoryDeployConfig(KernelConstants.JETTY_FACTORY_PID, webServerConfig);
@@ -90,12 +128,13 @@ class DeployConfig implements ConfigurationListener {
                save();
        }
 
-       private void init(ConfigurationAdmin configurationAdmin, boolean isClean) throws IOException {
+       private void init(ConfigurationAdmin configurationAdmin, boolean isClean, boolean isFirstInit) throws IOException {
 
                try (InputStream in = Files.newInputStream(deployConfigPath)) {
                        deployConfigs = new LdifParser().read(in);
                }
                if (isClean) {
+                       setFromFrameworkProperties(isFirstInit);
                        for (LdapName dn : deployConfigs.keySet()) {
                                Rdn lastRdn = dn.getRdn(dn.size() - 1);
                                LdapName prefix = (LdapName) dn.getPrefix(dn.size() - 1);
@@ -210,6 +249,10 @@ class DeployConfig implements ConfigurationListener {
                }
        }
 
+       boolean isStandalone(String dataModelName) {
+               return getProps(NodeConstants.NODE_REPOS_FACTORY_PID, dataModelName) != null;
+       }
+
        /*
         * UTILITIES
         */
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java
deleted file mode 100644 (file)
index 98c2483..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-package org.argeo.cms.internal.kernel;
-
-import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.URI;
-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.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.internal.http.HttpConstants;
-import org.argeo.cms.internal.jcr.RepoConf;
-import org.argeo.node.NodeConstants;
-import org.argeo.osgi.useradmin.UserAdminConf;
-
-/**
- * Interprets framework properties in order to generate the initial deploy
- * configuration.
- */
-class FirstInit {
-       private final static Log log = LogFactory.getLog(FirstInit.class);
-
-       public FirstInit() {
-               log.info("## FIRST INIT ##");
-       }
-
-       /** Override the provided config with the framework properties */
-       Dictionary<String, Object> getNodeRepositoryConfig(Dictionary<String, Object> provided) {
-               Dictionary<String, Object> props = provided != null ? provided : new Hashtable<String, Object>();
-               for (RepoConf repoConf : RepoConf.values()) {
-                       Object value = getFrameworkProp(NodeConstants.NODE_REPO_PROP_PREFIX + repoConf.name());
-                       if (value != null)
-                               props.put(repoConf.name(), value);
-               }
-               props.put(NodeConstants.CN, NodeConstants.NODE);
-               // props.put(NodeConstants.JCR_REPOSITORY_ALIAS, NodeConstants.NODE);
-               return props;
-       }
-
-       /** Override the provided config with the framework properties */
-       Dictionary<String, Object> getHttpServerConfig(Dictionary<String, Object> 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(HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.HTTP_HOST);
-               String httpsHost = getFrameworkProp(HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.HTTPS_HOST);
-
-               final Hashtable<String, Object> props = new Hashtable<String, Object>();
-               // try {
-               if (httpPort != null || httpsPort != null) {
-                       if (httpPort != null) {
-                               props.put(HttpConstants.HTTP_PORT, httpPort);
-                               props.put(HttpConstants.HTTP_ENABLED, true);
-                       }
-                       if (httpsPort != null) {
-                               props.put(HttpConstants.HTTPS_PORT, httpsPort);
-                               props.put(HttpConstants.HTTPS_ENABLED, true);
-                               Path keyStorePath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_KEYSTORE_PATH);
-                               String keyStorePassword = getFrameworkProp(
-                                               HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.SSL_PASSWORD);
-                               if (keyStorePassword == null)
-                                       keyStorePassword = "changeit";
-                               if (!Files.exists(keyStorePath))
-                                       createSelfSignedKeyStore(keyStorePath, keyStorePassword);
-                               props.put(HttpConstants.SSL_KEYSTORETYPE, "PKCS12");
-                               props.put(HttpConstants.SSL_KEYSTORE, keyStorePath.toString());
-                               props.put(HttpConstants.SSL_PASSWORD, keyStorePassword);
-                               props.put(HttpConstants.SSL_WANTCLIENTAUTH, true);
-                       }
-                       if (httpHost != null)
-                               props.put(HttpConstants.HTTP_HOST, httpHost);
-                       if (httpsHost != null)
-                               props.put(HttpConstants.HTTPS_HOST, httpHost);
-
-                       props.put(NodeConstants.CN, NodeConstants.DEFAULT);
-               }
-               return props;
-       }
-
-       List<Dictionary<String, Object>> getUserDirectoryConfigs() {
-               List<Dictionary<String, Object>> res = new ArrayList<>();
-               File nodeBaseDir = KernelUtils.getOsgiInstancePath(KernelConstants.DIR_NODE).toFile();
-               List<String> uris = new ArrayList<>();
-
-               // node roles
-               String nodeRolesUri = getFrameworkProp(NodeConstants.ROLES_URI);
-               String baseNodeRoleDn = NodeConstants.ROLES_BASEDN;
-               if (nodeRolesUri == null) {
-                       nodeRolesUri = baseNodeRoleDn + ".ldif";
-                       File nodeRolesFile = new File(nodeBaseDir, nodeRolesUri);
-                       if (!nodeRolesFile.exists())
-                               try {
-                                       FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(baseNodeRoleDn + ".ldif"),
-                                                       nodeRolesFile);
-                               } catch (IOException e) {
-                                       throw new CmsException("Cannot copy demo resource", e);
-                               }
-                       // nodeRolesUri = nodeRolesFile.toURI().toString();
-               }
-               uris.add(nodeRolesUri);
-
-               // Business roles
-               String userAdminUris = getFrameworkProp(NodeConstants.USERADMIN_URIS);
-               if (userAdminUris == null) {
-                       String demoBaseDn = "dc=example,dc=com";
-                       userAdminUris = demoBaseDn + ".ldif";
-                       File businessRolesFile = new File(nodeBaseDir, userAdminUris);
-                       if (!businessRolesFile.exists())
-                               try {
-                                       FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(demoBaseDn + ".ldif"),
-                                                       businessRolesFile);
-                               } catch (IOException e) {
-                                       throw new CmsException("Cannot copy demo resource", e);
-                               }
-                       // userAdminUris = businessRolesFile.toURI().toString();
-                       log.warn("## DEV Using dummy base DN " + demoBaseDn);
-                       // TODO downgrade security level
-               }
-               for (String userAdminUri : userAdminUris.split(" "))
-                       uris.add(userAdminUri);
-
-               // Interprets URIs
-               for (String uri : uris) {
-                       URI u;
-                       try {
-                               u = new URI(uri);
-                               if (u.getPath() == null)
-                                       throw new CmsException("URI " + uri + " must have a path in order to determine base DN");
-                               if (u.getScheme() == null) {
-                                       if (uri.startsWith("/") || uri.startsWith("./") || uri.startsWith("../"))
-                                               u = new File(uri).getCanonicalFile().toURI();
-                                       else if (!uri.contains("/")) {
-                                               // u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + uri);
-                                               u = new URI(uri);
-                                       } else
-                                               throw new CmsException("Cannot interpret " + uri + " as an uri");
-                               } else if (u.getScheme().equals(UserAdminConf.SCHEME_FILE)) {
-                                       u = new File(u).getCanonicalFile().toURI();
-                               }
-                       } catch (Exception e) {
-                               throw new CmsException("Cannot interpret " + uri + " as an uri", e);
-                       }
-                       Dictionary<String, Object> properties = UserAdminConf.uriAsProperties(u.toString());
-                       res.add(properties);
-               }
-
-               return res;
-       }
-
-       /**
-        * Called before node initialisation, in order populate OSGi instance are with
-        * some files (typically LDIF, etc).
-        */
-       static void prepareInstanceArea() {
-               String nodeInit = getFrameworkProp(NodeConstants.NODE_INIT);
-               if (nodeInit == null)
-                       nodeInit = "../../init";
-               if (nodeInit.startsWith("http")) {
-                       // remoteFirstInit(nodeInit);
-                       return;
-               }
-
-               // TODO use java.nio.file
-               File initDir;
-               if (nodeInit.startsWith("."))
-                       initDir = KernelUtils.getExecutionDir(nodeInit);
-               else
-                       initDir = new File(nodeInit);
-               // TODO also uncompress archives
-               if (initDir.exists())
-                       try {
-                               FileUtils.copyDirectory(initDir, KernelUtils.getOsgiInstanceDir(), new FileFilter() {
-
-                                       @Override
-                                       public boolean accept(File pathname) {
-                                               if (pathname.getName().equals(".svn") || pathname.getName().equals(".git"))
-                                                       return false;
-                                               return true;
-                                       }
-                               });
-                               log.info("CMS initialized from " + initDir.getCanonicalPath());
-                       } catch (IOException e) {
-                               throw new CmsException("Cannot initialize from " + initDir, e);
-                       }
-       }
-
-       private void createSelfSignedKeyStore(Path keyStorePath, String keyStorePassword) {
-               // for (Provider provider : Security.getProviders())
-               // System.out.println(provider.getName());
-               File keyStoreFile = keyStorePath.toFile();
-               char[] ksPwd = keyStorePassword.toCharArray();
-               char[] keyPwd = Arrays.copyOf(ksPwd, ksPwd.length);
-               if (!keyStoreFile.exists()) {
-                       try {
-                               keyStoreFile.getParentFile().mkdirs();
-                               KeyStore keyStore = PkiUtils.getKeyStore(keyStoreFile, ksPwd);
-                               PkiUtils.generateSelfSignedCertificate(keyStore,
-                                               new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
-                                               1024, keyPwd);
-                               PkiUtils.saveKeyStore(keyStoreFile, ksPwd, keyStore);
-                               if (log.isDebugEnabled())
-                                       log.debug("Created self-signed unsecure keystore " + keyStoreFile);
-                       } catch (Exception e) {
-                               if (keyStoreFile.length() == 0)
-                                       keyStoreFile.delete();
-                               log.error("Cannot create keystore " + keyStoreFile, e);
-                       }
-               } else {
-                       throw new CmsException("Keystore " + keyStorePath + " already exists");
-               }
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/InitUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/InitUtils.java
new file mode 100644 (file)
index 0000000..8a22b48
--- /dev/null
@@ -0,0 +1,237 @@
+package org.argeo.cms.internal.kernel;
+
+import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URI;
+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.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.internal.http.HttpConstants;
+import org.argeo.cms.internal.jcr.RepoConf;
+import org.argeo.node.NodeConstants;
+import org.argeo.osgi.useradmin.UserAdminConf;
+
+/**
+ * Interprets framework properties in order to generate the initial deploy
+ * configuration.
+ */
+class InitUtils {
+       private final static Log log = LogFactory.getLog(InitUtils.class);
+
+       /** Override the provided config with the framework properties */
+       static Dictionary<String, Object> getNodeRepositoryConfig(Dictionary<String, Object> provided) {
+               Dictionary<String, Object> props = provided != null ? provided : new Hashtable<String, Object>();
+               for (RepoConf repoConf : RepoConf.values()) {
+                       Object value = getFrameworkProp(NodeConstants.NODE_REPO_PROP_PREFIX + repoConf.name());
+                       if (value != null)
+                               props.put(repoConf.name(), value);
+               }
+               props.put(NodeConstants.CN, NodeConstants.NODE);
+               return props;
+       }
+
+       static Dictionary<String, Object> getRepositoryConfig(String dataModelName, Dictionary<String, Object> provided) {
+               if (dataModelName.equals(NodeConstants.NODE) || dataModelName.equals(NodeConstants.HOME))
+                       throw new IllegalArgumentException("Data model '" + dataModelName + "' is reserved.");
+               Dictionary<String, Object> props = provided != null ? provided : new Hashtable<String, Object>();
+               for (RepoConf repoConf : RepoConf.values()) {
+                       Object value = getFrameworkProp(
+                                       NodeConstants.NODE_REPOS_PROP_PREFIX + dataModelName + '.' + repoConf.name());
+                       if (value != null)
+                               props.put(repoConf.name(), value);
+               }
+               if (props.size() != 0)
+                       props.put(NodeConstants.CN, dataModelName);
+               return props;
+       }
+
+       /** Override the provided config with the framework properties */
+       static Dictionary<String, Object> getHttpServerConfig(Dictionary<String, Object> 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(HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.HTTP_HOST);
+               String httpsHost = getFrameworkProp(HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.HTTPS_HOST);
+
+               final Hashtable<String, Object> props = new Hashtable<String, Object>();
+               // try {
+               if (httpPort != null || httpsPort != null) {
+                       if (httpPort != null) {
+                               props.put(HttpConstants.HTTP_PORT, httpPort);
+                               props.put(HttpConstants.HTTP_ENABLED, true);
+                       }
+                       if (httpsPort != null) {
+                               props.put(HttpConstants.HTTPS_PORT, httpsPort);
+                               props.put(HttpConstants.HTTPS_ENABLED, true);
+                               Path keyStorePath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_KEYSTORE_PATH);
+                               String keyStorePassword = getFrameworkProp(
+                                               HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.SSL_PASSWORD);
+                               if (keyStorePassword == null)
+                                       keyStorePassword = "changeit";
+                               if (!Files.exists(keyStorePath))
+                                       createSelfSignedKeyStore(keyStorePath, keyStorePassword);
+                               props.put(HttpConstants.SSL_KEYSTORETYPE, "PKCS12");
+                               props.put(HttpConstants.SSL_KEYSTORE, keyStorePath.toString());
+                               props.put(HttpConstants.SSL_PASSWORD, keyStorePassword);
+                               props.put(HttpConstants.SSL_WANTCLIENTAUTH, true);
+                       }
+                       if (httpHost != null)
+                               props.put(HttpConstants.HTTP_HOST, httpHost);
+                       if (httpsHost != null)
+                               props.put(HttpConstants.HTTPS_HOST, httpHost);
+
+                       props.put(NodeConstants.CN, NodeConstants.DEFAULT);
+               }
+               return props;
+       }
+
+       static List<Dictionary<String, Object>> getUserDirectoryConfigs() {
+               List<Dictionary<String, Object>> res = new ArrayList<>();
+               File nodeBaseDir = KernelUtils.getOsgiInstancePath(KernelConstants.DIR_NODE).toFile();
+               List<String> uris = new ArrayList<>();
+
+               // node roles
+               String nodeRolesUri = getFrameworkProp(NodeConstants.ROLES_URI);
+               String baseNodeRoleDn = NodeConstants.ROLES_BASEDN;
+               if (nodeRolesUri == null) {
+                       nodeRolesUri = baseNodeRoleDn + ".ldif";
+                       File nodeRolesFile = new File(nodeBaseDir, nodeRolesUri);
+                       if (!nodeRolesFile.exists())
+                               try {
+                                       FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(baseNodeRoleDn + ".ldif"),
+                                                       nodeRolesFile);
+                               } catch (IOException e) {
+                                       throw new CmsException("Cannot copy demo resource", e);
+                               }
+                       // nodeRolesUri = nodeRolesFile.toURI().toString();
+               }
+               uris.add(nodeRolesUri);
+
+               // Business roles
+               String userAdminUris = getFrameworkProp(NodeConstants.USERADMIN_URIS);
+               if (userAdminUris == null) {
+                       String demoBaseDn = "dc=example,dc=com";
+                       userAdminUris = demoBaseDn + ".ldif";
+                       File businessRolesFile = new File(nodeBaseDir, userAdminUris);
+                       if (!businessRolesFile.exists())
+                               try {
+                                       FileUtils.copyInputStreamToFile(InitUtils.class.getResourceAsStream(demoBaseDn + ".ldif"),
+                                                       businessRolesFile);
+                               } catch (IOException e) {
+                                       throw new CmsException("Cannot copy demo resource", e);
+                               }
+                       // userAdminUris = businessRolesFile.toURI().toString();
+                       log.warn("## DEV Using dummy base DN " + demoBaseDn);
+                       // TODO downgrade security level
+               }
+               for (String userAdminUri : userAdminUris.split(" "))
+                       uris.add(userAdminUri);
+
+               // Interprets URIs
+               for (String uri : uris) {
+                       URI u;
+                       try {
+                               u = new URI(uri);
+                               if (u.getPath() == null)
+                                       throw new CmsException("URI " + uri + " must have a path in order to determine base DN");
+                               if (u.getScheme() == null) {
+                                       if (uri.startsWith("/") || uri.startsWith("./") || uri.startsWith("../"))
+                                               u = new File(uri).getCanonicalFile().toURI();
+                                       else if (!uri.contains("/")) {
+                                               // u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + uri);
+                                               u = new URI(uri);
+                                       } else
+                                               throw new CmsException("Cannot interpret " + uri + " as an uri");
+                               } else if (u.getScheme().equals(UserAdminConf.SCHEME_FILE)) {
+                                       u = new File(u).getCanonicalFile().toURI();
+                               }
+                       } catch (Exception e) {
+                               throw new CmsException("Cannot interpret " + uri + " as an uri", e);
+                       }
+                       Dictionary<String, Object> properties = UserAdminConf.uriAsProperties(u.toString());
+                       res.add(properties);
+               }
+
+               return res;
+       }
+
+       /**
+        * Called before node initialisation, in order populate OSGi instance are with
+        * some files (typically LDIF, etc).
+        */
+       static void prepareFirstInitInstanceArea() {
+               String nodeInit = getFrameworkProp(NodeConstants.NODE_INIT);
+               if (nodeInit == null)
+                       nodeInit = "../../init";
+               if (nodeInit.startsWith("http")) {
+                       // remoteFirstInit(nodeInit);
+                       return;
+               }
+
+               // TODO use java.nio.file
+               File initDir;
+               if (nodeInit.startsWith("."))
+                       initDir = KernelUtils.getExecutionDir(nodeInit);
+               else
+                       initDir = new File(nodeInit);
+               // TODO also uncompress archives
+               if (initDir.exists())
+                       try {
+                               FileUtils.copyDirectory(initDir, KernelUtils.getOsgiInstanceDir(), new FileFilter() {
+
+                                       @Override
+                                       public boolean accept(File pathname) {
+                                               if (pathname.getName().equals(".svn") || pathname.getName().equals(".git"))
+                                                       return false;
+                                               return true;
+                                       }
+                               });
+                               log.info("CMS initialized from " + initDir.getCanonicalPath());
+                       } catch (IOException e) {
+                               throw new CmsException("Cannot initialize from " + initDir, e);
+                       }
+       }
+
+       private static void createSelfSignedKeyStore(Path keyStorePath, String keyStorePassword) {
+               // for (Provider provider : Security.getProviders())
+               // System.out.println(provider.getName());
+               File keyStoreFile = keyStorePath.toFile();
+               char[] ksPwd = keyStorePassword.toCharArray();
+               char[] keyPwd = Arrays.copyOf(ksPwd, ksPwd.length);
+               if (!keyStoreFile.exists()) {
+                       try {
+                               keyStoreFile.getParentFile().mkdirs();
+                               KeyStore keyStore = PkiUtils.getKeyStore(keyStoreFile, ksPwd);
+                               PkiUtils.generateSelfSignedCertificate(keyStore,
+                                               new X500Principal("CN=" + InetAddress.getLocalHost().getHostName() + ",OU=UNSECURE,O=UNSECURE"),
+                                               1024, keyPwd);
+                               PkiUtils.saveKeyStore(keyStoreFile, ksPwd, keyStore);
+                               if (log.isDebugEnabled())
+                                       log.debug("Created self-signed unsecure keystore " + keyStoreFile);
+                       } catch (Exception e) {
+                               if (keyStoreFile.length() == 0)
+                                       keyStoreFile.delete();
+                               log.error("Cannot create keystore " + keyStoreFile, e);
+                       }
+               } else {
+                       throw new CmsException("Keystore " + keyStorePath + " already exists");
+               }
+       }
+
+}
index 63cb356d33fca383b2c90f782036eb896cc62fe7..1d81409115b9bcba31ae862117c18c2a728df571 100644 (file)
@@ -25,6 +25,7 @@ import javax.security.auth.login.LoginException;
 
 import org.apache.commons.logging.Log;
 import org.argeo.cms.CmsException;
+import org.argeo.node.DataModelNamespace;
 import org.argeo.node.NodeConstants;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -206,7 +207,7 @@ class KernelUtils implements KernelConstants {
         * @throws CmsException
         *             if the related bundle is not active
         */
-       public static BundleContext getBundleContext(Class<?> clzz) {
+       static BundleContext getBundleContext(Class<?> clzz) {
                Bundle bundle = FrameworkUtil.getBundle(clzz);
                BundleContext bc = bundle.getBundleContext();
                if (bc == null)
@@ -214,10 +215,24 @@ class KernelUtils implements KernelConstants {
                return bc;
        }
 
-       private static BundleContext getBundleContext() {
+       static BundleContext getBundleContext() {
                return getBundleContext(KernelUtils.class);
        }
 
+       static boolean asBoolean(String value) {
+               if (value == null)
+                       return false;
+               switch (value) {
+               case "true":
+                       return true;
+               case "false":
+                       return false;
+               default:
+                       throw new CmsException("Unsupported value for attribute " + DataModelNamespace.ABSTRACT
+                                       + ": " + value);
+               }
+       }
+
        private static URI safeUri(String uri) {
                if (uri == null)
                        throw new CmsException("URI cannot be null");
index 67c66e915302e898c87749ee37bdb38688a85162..6211ccd37a904219836b186e57b149d5e94c864f 100644 (file)
@@ -15,7 +15,7 @@ class LocalRepository extends JcrRepositoryWrapper {
        public LocalRepository(Repository repository, BundleCapability dataModelCapability) {
                super(repository);
                Map<String, Object> attrs = dataModelCapability.getAttributes();
-               cn = (String) attrs.get(DataModelNamespace.CAPABILITY_NAME_ATTRIBUTE);
+               cn = (String) attrs.get(DataModelNamespace.NAME);
                putDescriptor(NodeConstants.CN, cn);
        }
 
index 56f2f5c170bdc67719c2dcde9c28935065fa1a47..95b1f07adec9705e2802df0e2fea989ea9d2f003 100644 (file)
@@ -54,6 +54,7 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
        private final String userObjectClass, userBase, groupObjectClass, groupBase;
 
        private final boolean readOnly;
+       private final boolean disabled;
        private final URI uri;
 
        private UserAdmin externalRoles;
@@ -108,6 +109,11 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
                        properties.put(UserAdminConf.readOnly.name(), Boolean.toString(readOnly));
                } else
                        readOnly = new Boolean(readOnlyStr);
+               String disabledStr = UserAdminConf.disabled.getValue(properties);
+               if (disabledStr != null)
+                       disabled = new Boolean(disabledStr);
+               else
+                       disabled = false;
        }
 
        /** Returns the groups this user is a direct member of. */
@@ -438,6 +444,10 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory
                return readOnly;
        }
 
+       public boolean isDisabled() {
+               return disabled;
+       }
+
        protected UserAdmin getExternalRoles() {
                return externalRoles;
        }
index d2054416bba2703e1a1a4aeebff261da9d87beee..f7a7c6e478ba8197e9ef414e5c7fce8e5a20f8aa 100644 (file)
@@ -139,8 +139,11 @@ public class AggregatingUserAdmin implements UserAdmin {
                        return systemRoles;
                List<UserAdmin> res = new ArrayList<UserAdmin>(1);
                for (LdapName baseDn : businessRoles.keySet()) {
-                       if (name.startsWith(baseDn))
-                               res.add(businessRoles.get(baseDn));
+                       if (name.startsWith(baseDn)) {
+                               AbstractUserDirectory ud = businessRoles.get(baseDn);
+                               if (!ud.isDisabled())
+                                       res.add(ud);
+                       }
                }
                if (res.size() == 0)
                        throw new UserDirectoryException("Cannot find user admin for " + name);
index f6a237bcc0e4e355dd464af70c180c49bfeb45ff..51d18349b2a662a762871e9464c5e24370a01c77 100644 (file)
@@ -42,7 +42,28 @@ class DigestUtils {
                return bytes;
        }
 
-       private DigestUtils() {
+       static String sha1str(String str) {
+               byte[] hash = sha1(str.getBytes(StandardCharsets.UTF_8));
+               return encodeHexString(hash);
        }
 
+       final private static char[] hexArray = "0123456789abcdef".toCharArray();
+
+       /**
+        * From
+        * http://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to
+        * -a-hex-string-in-java
+        */
+       public static String encodeHexString(byte[] bytes) {
+               char[] hexChars = new char[bytes.length * 2];
+               for (int j = 0; j < bytes.length; j++) {
+                       int v = bytes[j] & 0xFF;
+                       hexChars[j * 2] = hexArray[v >>> 4];
+                       hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+               }
+               return new String(hexChars);
+       }
+
+       private DigestUtils() {
+       }
 }
index b3ead140c7b5aa641418d5ec54ca204ed853f585..37d6339208486d3d5ec07b428262ac6442a826f3 100644 (file)
@@ -13,6 +13,7 @@ import java.util.Map;
 
 import javax.naming.Context;
 import javax.naming.NamingException;
+import javax.naming.ldap.LdapName;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -43,6 +44,9 @@ public enum UserAdminConf {
        /** Read-only source */
        readOnly(null),
 
+       /** Disabled source */
+       disabled(null),
+
        /** Authentication realm */
        realm(null);
 
@@ -144,6 +148,9 @@ public enum UserAdminConf {
                        if (bDn.endsWith(".ldif"))
                                bDn = bDn.substring(0, bDn.length() - ".ldif".length());
 
+                       // Normalize base DN as LDAP name
+                       bDn = new LdapName(bDn).toString();
+
                        String principal = null;
                        String credentials = null;
                        if (scheme != null)
@@ -254,4 +261,15 @@ public enum UserAdminConf {
                        return "dc=" + hostname;
                }
        }
+
+       /**
+        * Hash the base DN in order to have a deterministic string to be used as a cn
+        * for the underlying user directory.
+        */
+       public static String baseDnHash(Dictionary<String, Object> properties) {
+               String bDn = (String) properties.get(baseDn.name());
+               if (bDn == null)
+                       throw new UserDirectoryException("No baseDn in " + properties);
+               return DigestUtils.sha1str(bDn);
+       }
 }
index e5de738363249090a1aea8f846cb4d339af6cc35..ff80c5ac8385bf4aacac7699102754004cbdb6a6 100644 (file)
@@ -6,18 +6,20 @@ import javax.transaction.xa.XAResource;
 /** Information about a user directory. */
 public interface UserDirectory {
        /** The base DN of all entries in this user directory */
-       public LdapName getBaseDn();
+       LdapName getBaseDn();
 
        /** The related {@link XAResource} */
-       public XAResource getXaResource();
+       XAResource getXaResource();
 
-       public boolean isReadOnly();
+       boolean isReadOnly();
 
-       public String getUserObjectClass();
+       boolean isDisabled();
 
-       public String getUserBase();
+       String getUserObjectClass();
 
-       public String getGroupObjectClass();
+       String getUserBase();
 
-       public String getGroupBase();
+       String getGroupObjectClass();
+
+       String getGroupBase();
 }
index 6da250dbf4466153c7033bf866a34f4164ddb08e..58e4a645adda908bb6145cc061ddf8d39c5e0ea8 100644 (file)
@@ -6,15 +6,10 @@ import org.osgi.resource.Namespace;
 public class DataModelNamespace extends Namespace {
 
        public static final String CMS_DATA_MODEL_NAMESPACE = "cms.datamodel";
-       public static final String CAPABILITY_NAME_ATTRIBUTE = "name";
-       public static final String CAPABILITY_CND_ATTRIBUTE = "cnd";
+       public static final String NAME = "name";
+       public static final String CND = "cnd";
        /** If 'true', indicates that no repository should be published */
-       public static final String CAPABILITY_ABSTRACT_ATTRIBUTE = "abstract";
-       /**
-        * If 'true', indicates that code using this data model should be prepared
-        * to have it stored in a different JCR repository than the node
-        */
-       public static final String CAPABILITY_STANDALONE_ATTRIBUTE = "standalone";
+       public static final String ABSTRACT = "abstract";
 
        private DataModelNamespace() {
                // empty
index 22afe0065e2e0d220f4e8e917da35b75cc00fd67..75b7826c999e032fdeef1f5ef00af84c0c81082c 100644 (file)
@@ -90,6 +90,8 @@ public interface NodeConstants {
        // Node
        /** Properties configuring the node repository */
        String NODE_REPO_PROP_PREFIX = "argeo.node.repo.";
+       /** Additional standalone repositories, related to data models. */
+       String NODE_REPOS_PROP_PREFIX = "argeo.node.repos.";
        // HTTP
        String HTTP_PORT = "org.osgi.service.http.port";
        String HTTP_PORT_SECURE = "org.osgi.service.http.port.secure";