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;
cleanState = nodeState.isClean();
nodeHttp = new NodeHttp();
+ dataModels = new DataModels(bc);
initTrackers();
}
@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
}
// 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);
}
/** 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");
}
// 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()) {
}
}
- 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);
}
}
- 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;
@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
--- /dev/null
+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;
+ }
+
+ }
+
+}
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;
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;
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);
}
}
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<>();
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);
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);
}
}
+ boolean isStandalone(String dataModelName) {
+ return getProps(NodeConstants.NODE_REPOS_FACTORY_PID, dataModelName) != null;
+ }
+
/*
* UTILITIES
*/
+++ /dev/null
-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");
- }
- }
-
-}
--- /dev/null
+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");
+ }
+ }
+
+}
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;
* @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)
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");
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);
}
private final String userObjectClass, userBase, groupObjectClass, groupBase;
private final boolean readOnly;
+ private final boolean disabled;
private final URI uri;
private UserAdmin externalRoles;
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. */
return readOnly;
}
+ public boolean isDisabled() {
+ return disabled;
+ }
+
protected UserAdmin getExternalRoles() {
return externalRoles;
}
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);
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() {
+ }
}
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;
/** Read-only source */
readOnly(null),
+ /** Disabled source */
+ disabled(null),
+
/** Authentication realm */
realm(null);
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)
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);
+ }
}
/** 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();
}
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
// 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";