From: Mathieu Baudier Date: Wed, 20 Jul 2022 09:37:07 +0000 (+0200) Subject: Make CMS running without data area, and remove unnecessary dependencies. X-Git-Tag: v2.3.10~104 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=f3ea14abccc33b1c3326417a87c91145be776c72;p=lgpl%2Fargeo-commons.git Make CMS running without data area, and remove unnecessary dependencies. --- diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/CmsConstants.java b/org.argeo.api.cms/src/org/argeo/api/cms/CmsConstants.java index 207b0a8df..98364d97b 100644 --- a/org.argeo.api.cms/src/org/argeo/api/cms/CmsConstants.java +++ b/org.argeo.api.cms/src/org/argeo/api/cms/CmsConstants.java @@ -86,33 +86,9 @@ public interface CmsConstants { String EVENT_TOPICS = "event.topics"; /* - * INIT FRAMEWORK PROPERTIES + * FILE SYSTEM */ -// String NODE_INIT = "argeo.node.init"; -// String I18N_DEFAULT_LOCALE = "argeo.i18n.defaultLocale"; -// String I18N_LOCALES = "argeo.i18n.locales"; - // Node Security -// String ROLES_URI = "argeo.node.roles.uri"; -// String TOKENS_URI = "argeo.node.tokens.uri"; -// /** URI to an LDIF file or LDAP server used as initialization or backend */ -// String USERADMIN_URIS = "argeo.node.useradmin.uris"; - // Transaction manager -// String TRANSACTION_MANAGER = "argeo.node.transaction.manager"; -// String TRANSACTION_MANAGER_SIMPLE = "simple"; -// String TRANSACTION_MANAGER_BITRONIX = "bitronix"; - // 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"; -// /** -// * The HTTP header used to convey the DN of a client verified by a reverse -// * proxy. Typically SSL_CLIENT_S_DN for Apache. -// */ -// String HTTP_PROXY_SSL_DN = "argeo.http.proxy.ssl.dn"; + String CMS_FS_SCHEME = "cms"; /* * PIDs diff --git a/org.argeo.cms/src/org/argeo/cms/acr/MountManager.java b/org.argeo.cms/src/org/argeo/cms/acr/MountManager.java index 6a6dcaacb..69b76ddc6 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/MountManager.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/MountManager.java @@ -52,6 +52,8 @@ class MountManager { if (entry == null) throw new IllegalArgumentException("No entry provider found for " + path); String mountPath = entry.getKey(); + if (!path.startsWith(mountPath)) + throw new IllegalArgumentException("Path " + path + " doesn't have a content provider"); ContentProvider contentProvider = entry.getValue(); assert mountPath.equals(contentProvider.getMountPath()); return contentProvider; diff --git a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java index 54013e2ad..d6e246df5 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java @@ -15,6 +15,7 @@ import javax.xml.xpath.XPathFactory; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentNotFoundException; +import org.argeo.api.acr.CrName; import org.argeo.api.acr.NamespaceUtils; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedContent; @@ -79,7 +80,7 @@ public class DomContentProvider implements ContentProvider, NamespaceContext { throw new IllegalArgumentException("Relative path cannot start with /"); String xPathExpression = '/' + relativePath; if ("/".equals(mountPath)) - xPathExpression = "/cr:root" + xPathExpression; + xPathExpression = "/" + CrName.root.qName() + xPathExpression; try { NodeList nodes = (NodeList) xPath.get().evaluate(xPathExpression, document, XPathConstants.NODESET); return nodes; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java index ea9a401a4..7a0f3388c 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java @@ -3,7 +3,6 @@ package org.argeo.cms.internal.runtime; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.HashMap; -import java.util.IdentityHashMap; import java.util.List; import java.util.Locale; import java.util.Map; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java index d335b48b1..fd2c5f9cd 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java @@ -1,7 +1,5 @@ package org.argeo.cms.internal.runtime; -import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.io.Reader; import java.net.URL; @@ -23,13 +21,13 @@ import java.util.UUID; import javax.security.auth.login.Configuration; -import org.apache.commons.io.FileUtils; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsState; import org.argeo.api.uuid.UuidFactory; import org.argeo.cms.CmsDeployProperty; import org.argeo.cms.auth.ident.IdentClient; +import org.argeo.util.FsUtils; /** * Implementation of a {@link CmsState}, initialising the required services. @@ -57,12 +55,16 @@ public class CmsStateImpl implements CmsState { deployPropertyDefaults.put(CmsDeployProperty.SSL_KEYSTORETYPE, PkiUtils.PKCS12); deployPropertyDefaults.put(CmsDeployProperty.SSL_PASSWORD, PkiUtils.DEFAULT_KEYSTORE_PASSWORD); Path keyStorePath = getDataPath(PkiUtils.DEFAULT_KEYSTORE_PATH); - deployPropertyDefaults.put(CmsDeployProperty.SSL_KEYSTORE, keyStorePath.toAbsolutePath().toString()); + if (keyStorePath != null) { + deployPropertyDefaults.put(CmsDeployProperty.SSL_KEYSTORE, keyStorePath.toAbsolutePath().toString()); + } Path trustStorePath = getDataPath(PkiUtils.DEFAULT_TRUSTSTORE_PATH); + if (trustStorePath != null) { + deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTORE, trustStorePath.toAbsolutePath().toString()); + } deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTORETYPE, PkiUtils.PKCS12); deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD, PkiUtils.DEFAULT_KEYSTORE_PASSWORD); - deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTORE, trustStorePath.toAbsolutePath().toString()); this.deployPropertyDefaults = Collections.unmodifiableMap(deployPropertyDefaults); } @@ -110,9 +112,8 @@ public class CmsStateImpl implements CmsState { log.debug("## CMS starting... (" + uuid + ")\n" + sb + "\n"); } -// initI18n(); -// initServices(); - if (!Files.exists(getDataPath(CmsConstants.NODE))) {// first init + Path nodeBase = getDataPath(CmsConstants.NODE); + if (nodeBase != null && !Files.exists(nodeBase)) {// first init firstInit(); } @@ -351,7 +352,7 @@ public class CmsStateImpl implements CmsState { public static void prepareFirstInitInstanceArea(List nodeInits) { for (String nodeInit : nodeInits) { - if(nodeInit==null) + if (nodeInit == null) continue; if (nodeInit.startsWith("http")) { @@ -360,29 +361,17 @@ public class CmsStateImpl implements CmsState { } else { // TODO use java.nio.file - File initDir; + Path initDir; if (nodeInit.startsWith(".")) initDir = KernelUtils.getExecutionDir(nodeInit); else - initDir = new File(nodeInit); + initDir = Paths.get(nodeInit); // TODO also uncompress archives - if (initDir.exists()) - try { - // TODO use NIO utilities - FileUtils.copyDirectory(initDir, KernelUtils.getOsgiInstancePath("").toFile(), - 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 RuntimeException("Cannot initialize from " + initDir, e); - } + if (Files.exists(initDir)) { + Path dataPath = KernelUtils.getOsgiInstancePath(""); + FsUtils.copyDirectory(initDir, dataPath); + log.info("CMS initialized from " + initDir); + } } } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java index 4f0ba10aa..ab98c0625 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUserAdmin.java @@ -1,6 +1,5 @@ package org.argeo.cms.internal.runtime; -import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.URI; @@ -8,6 +7,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Dictionary; @@ -25,7 +25,6 @@ import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import org.apache.commons.io.FileUtils; import org.argeo.api.cms.CmsAuth; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsLog; @@ -89,42 +88,42 @@ public class CmsUserAdmin extends AggregatingUserAdmin { protected List> getUserDirectoryConfigs() { List> res = new ArrayList<>(); - File nodeBaseDir = cmsState.getDataPath(KernelConstants.DIR_NODE).toFile(); + Path nodeBase = cmsState.getDataPath(KernelConstants.DIR_NODE); List uris = new ArrayList<>(); // node roles String nodeRolesUri = null;// getFrameworkProp(CmsConstants.ROLES_URI); String baseNodeRoleDn = CmsConstants.ROLES_BASEDN; - if (nodeRolesUri == null) { + if (nodeRolesUri == null && nodeBase != null) { nodeRolesUri = baseNodeRoleDn + ".ldif"; - File nodeRolesFile = new File(nodeBaseDir, nodeRolesUri); - if (!nodeRolesFile.exists()) + Path nodeRolesFile = nodeBase.resolve(nodeRolesUri); + if (!Files.exists(nodeRolesFile)) try { - FileUtils.copyInputStreamToFile(CmsUserAdmin.class.getResourceAsStream(baseNodeRoleDn + ".ldif"), - nodeRolesFile); + Files.copy(CmsUserAdmin.class.getResourceAsStream(baseNodeRoleDn + ".ldif"), nodeRolesFile); } catch (IOException e) { throw new RuntimeException("Cannot copy demo resource", e); } // nodeRolesUri = nodeRolesFile.toURI().toString(); } - uris.add(nodeRolesUri); + if (nodeRolesUri != null) + uris.add(nodeRolesUri); // node tokens String nodeTokensUri = null;// getFrameworkProp(CmsConstants.TOKENS_URI); String baseNodeTokensDn = CmsConstants.TOKENS_BASEDN; - if (nodeTokensUri == null) { + if (nodeTokensUri == null && nodeBase != null) { nodeTokensUri = baseNodeTokensDn + ".ldif"; - File nodeTokensFile = new File(nodeBaseDir, nodeTokensUri); - if (!nodeTokensFile.exists()) + Path nodeTokensFile = nodeBase.resolve(nodeTokensUri); + if (!Files.exists(nodeTokensFile)) try { - FileUtils.copyInputStreamToFile(CmsUserAdmin.class.getResourceAsStream(baseNodeTokensDn + ".ldif"), - nodeTokensFile); + Files.copy(CmsUserAdmin.class.getResourceAsStream(baseNodeTokensDn + ".ldif"), nodeTokensFile); } catch (IOException e) { throw new RuntimeException("Cannot copy demo resource", e); } // nodeRolesUri = nodeRolesFile.toURI().toString(); } - uris.add(nodeTokensUri); + if (nodeTokensUri != null) + uris.add(nodeTokensUri); // Business roles // String userAdminUris = getFrameworkProp(CmsConstants.USERADMIN_URIS); @@ -136,19 +135,17 @@ public class CmsUserAdmin extends AggregatingUserAdmin { uris.add(userAdminUri); } - if (uris.size() == 0) { + if (uris.size() == 0 && nodeBase != null) { // TODO put this somewhere else String demoBaseDn = "dc=example,dc=com"; String userAdminUri = demoBaseDn + ".ldif"; - File businessRolesFile = new File(nodeBaseDir, userAdminUri); - File systemRolesFile = new File(nodeBaseDir, "ou=roles,ou=node.ldif"); - if (!businessRolesFile.exists()) + Path businessRolesFile = nodeBase.resolve(userAdminUri); + Path systemRolesFile = nodeBase.resolve("ou=roles,ou=node.ldif"); + if (!Files.exists(businessRolesFile)) try { - FileUtils.copyInputStreamToFile(CmsUserAdmin.class.getResourceAsStream(demoBaseDn + ".ldif"), - businessRolesFile); - if (!systemRolesFile.exists()) - FileUtils.copyInputStreamToFile( - CmsUserAdmin.class.getResourceAsStream("example-ou=roles,ou=node.ldif"), + Files.copy(CmsUserAdmin.class.getResourceAsStream(demoBaseDn + ".ldif"), businessRolesFile); + if (!Files.exists(systemRolesFile)) + Files.copy(CmsUserAdmin.class.getResourceAsStream("example-ou=roles,ou=node.ldif"), systemRolesFile); } catch (IOException e) { throw new RuntimeException("Cannot copy demo resources", e); @@ -168,14 +165,14 @@ public class CmsUserAdmin extends AggregatingUserAdmin { "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(); + u = Paths.get(uri).toRealPath().toUri(); else if (!uri.contains("/")) { // u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + uri); u = new URI(uri); } else throw new IllegalArgumentException("Cannot interpret " + uri + " as an uri"); } else if (u.getScheme().equals(DirectoryConf.SCHEME_FILE)) { - u = new File(u).getCanonicalFile().toURI(); + u = Paths.get(u).toRealPath().toUri(); } } catch (Exception e) { throw new RuntimeException("Cannot interpret " + uri + " as an uri", e); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java index 72a30fb08..8358105e2 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java @@ -31,9 +31,11 @@ public class DeployedContentRepository extends CmsContentRepository { // run dir Path runDirPath = KernelUtils.getOsgiInstancePath(CmsContentRepository.RUN_BASE); - Files.createDirectories(runDirPath); - FsContentProvider runContentProvider = new FsContentProvider(CmsContentRepository.RUN_BASE, runDirPath); - addProvider(runContentProvider); + if (runDirPath != null) { + Files.createDirectories(runDirPath); + FsContentProvider runContentProvider = new FsContentProvider(CmsContentRepository.RUN_BASE, runDirPath); + addProvider(runContentProvider); + } // users DirectoryContentProvider directoryContentProvider = new DirectoryContentProvider( diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelUtils.java index 0e84af62a..295578f53 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelUtils.java @@ -1,6 +1,5 @@ package org.argeo.cms.internal.runtime; -import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.net.URI; @@ -52,12 +51,12 @@ class KernelUtils implements KernelConstants { return asDictionary(props); } - static File getExecutionDir(String relativePath) { - File executionDir = new File(getFrameworkProp("user.dir")); + static Path getExecutionDir(String relativePath) { + Path executionDir = Paths.get(getFrameworkProp("user.dir")); if (relativePath == null) return executionDir; try { - return new File(executionDir, relativePath).getCanonicalFile(); + return executionDir.resolve(relativePath).toRealPath(); } catch (IOException e) { throw new IllegalArgumentException("Cannot get canonical file", e); } @@ -69,28 +68,34 @@ class KernelUtils implements KernelConstants { // } public static Path getOsgiInstancePath(String relativePath) { - return Paths.get(getOsgiInstanceUri(relativePath)); + URI uri = getOsgiInstanceUri(relativePath); + if (uri == null) // no data area available + return null; + return Paths.get(uri); } public static URI getOsgiInstanceUri(String relativePath) { String osgiInstanceBaseUri = getFrameworkProp(OSGI_INSTANCE_AREA); + if (osgiInstanceBaseUri == null) // no data area available + return null; + if (!osgiInstanceBaseUri.endsWith("/")) osgiInstanceBaseUri = osgiInstanceBaseUri + "/"; - if (osgiInstanceBaseUri != null) - return safeUri(osgiInstanceBaseUri + (relativePath != null ? relativePath : "")); - else - return Paths.get(System.getProperty("user.dir"), (relativePath != null ? relativePath : "")).toUri(); +// if (osgiInstanceBaseUri != null) + return safeUri(osgiInstanceBaseUri + (relativePath != null ? relativePath : "")); +// else +// return Paths.get(System.getProperty("user.dir"), (relativePath != null ? relativePath : "")).toUri(); } - static File getOsgiConfigurationFile(String relativePath) { - try { - return new File( - new URI(CmsActivator.getBundleContext().getProperty(OSGI_CONFIGURATION_AREA) + relativePath)) - .getCanonicalFile(); - } catch (Exception e) { - throw new IllegalArgumentException("Cannot get configuration file for " + relativePath, e); - } - } +// static File getOsgiConfigurationFile(String relativePath) { +// try { +// return new File( +// new URI(CmsActivator.getBundleContext().getProperty(OSGI_CONFIGURATION_AREA) + relativePath)) +// .getCanonicalFile(); +// } catch (Exception e) { +// throw new IllegalArgumentException("Cannot get configuration file for " + relativePath, e); +// } +// } static String getFrameworkProp(String key, String def) { String value; diff --git a/org.argeo.cms/src/org/argeo/cms/osgi/BundleCmsTheme.java b/org.argeo.cms/src/org/argeo/cms/osgi/BundleCmsTheme.java index 3ab799f71..de7eec8c2 100644 --- a/org.argeo.cms/src/org/argeo/cms/osgi/BundleCmsTheme.java +++ b/org.argeo.cms/src/org/argeo/cms/osgi/BundleCmsTheme.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; @@ -17,8 +16,8 @@ import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; -import org.apache.commons.io.IOUtils; import org.argeo.api.cms.ux.CmsTheme; +import org.argeo.util.StreamUtils; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -213,7 +212,7 @@ public class BundleCmsTheme implements CmsTheme { void loadBodyHtml(URL url) { try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) { - bodyHtml = IOUtils.toString(url, StandardCharsets.UTF_8); + bodyHtml = StreamUtils.toString(in); } catch (IOException e) { throw new IllegalArgumentException("Cannot load URL " + url, e); } diff --git a/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java b/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java index d1a19bd63..e473d2799 100644 --- a/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java +++ b/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java @@ -31,7 +31,7 @@ import org.osgi.service.useradmin.UserAdmin; * deployment. Useful for testing or AOT compilation. */ public class StaticCms { - private static SimpleRegister register = new SimpleRegister(); + private SimpleRegister register = new SimpleRegister(); private CompletableFuture stopped = new CompletableFuture(); @@ -51,14 +51,6 @@ public class StaticCms { .addDependency(uuidFactoryC.getType(UuidFactory.class), cmsState::setUuidFactory, null) // .build(register); - // Deployment Configuration -// DeployConfig deployConfig = new DeployConfig(); -// Component deployConfigC = new Component.Builder<>(deployConfig) // -// .addType(DeployConfig.class) // -// .addActivation(deployConfig::start) // -// .addDeactivation(deployConfig::stop) // -// .build(register); - // Transaction manager SimpleTransactionManager transactionManager = new SimpleTransactionManager(); Component transactionManagerC = new Component.Builder<>(transactionManager) // @@ -75,10 +67,6 @@ public class StaticCms { .addDependency(cmsStateC.getType(CmsState.class), userAdmin::setCmsState, null) // .addDependency(transactionManagerC.getType(WorkControl.class), userAdmin::setTransactionManager, null) // .addDependency(transactionManagerC.getType(WorkTransaction.class), userAdmin::setUserTransaction, null) // -// .addDependency(deployConfigC.getType(DeployConfig.class), (d) -> { -// for (Dictionary userDirectoryConfig : d.getUserDirectoryConfigs()) -// userAdmin.enableUserDirectory(userDirectoryConfig); -// }, null) // .build(register); // User manager diff --git a/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java b/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java index 10b583fdf..3de2e1451 100644 --- a/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java +++ b/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java @@ -24,9 +24,9 @@ import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import org.apache.commons.io.IOUtils; import org.argeo.api.cms.CmsAuth; import org.argeo.util.CurrentSubject; +import org.argeo.util.StreamUtils; /** username / password based keyring. TODO internationalize */ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { @@ -109,7 +109,7 @@ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { try (InputStream in = getAsStream(path); CharArrayWriter writer = new CharArrayWriter(); Reader reader = new InputStreamReader(in, charset);) { - IOUtils.copy(reader, writer); + StreamUtils.copy(reader, writer); return writer.toCharArray(); } catch (IOException e) { throw new IllegalStateException("Cannot decrypt to char array", e); diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java index c1727f746..ef253800c 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; @@ -14,6 +15,7 @@ import java.util.TreeSet; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; +import org.argeo.util.directory.DirectoryConf; import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.useradmin.Authorization; import org.osgi.service.useradmin.Group; @@ -251,7 +253,12 @@ public class AggregatingUserAdmin implements UserAdmin { // } public void start() { - + if (systemRoles == null) { + // TODO do we really need separate system roles? + Hashtable properties = new Hashtable<>(); + properties.put(DirectoryConf.baseDn.name(), "ou=roles,ou=system"); + systemRoles = new DirectoryUserAdmin(properties); + } } public void stop() { diff --git a/org.argeo.util/src/org/argeo/util/FsUtils.java b/org.argeo.util/src/org/argeo/util/FsUtils.java index b317f4bc9..23839db74 100644 --- a/org.argeo.util/src/org/argeo/util/FsUtils.java +++ b/org.argeo.util/src/org/argeo/util/FsUtils.java @@ -9,6 +9,43 @@ import java.nio.file.attribute.BasicFileAttributes; /** Utilities around the standard Java file abstractions. */ public class FsUtils { + + /** + * Deletes this path, recursively if needed. Does nothing if the path does not + * exist. + */ + public static void copyDirectory(Path source, Path target) { + if (!Files.exists(source) || !Files.isDirectory(source)) + throw new IllegalArgumentException(source + " is not a directory"); + if (Files.exists(target) && !Files.isDirectory(target)) + throw new IllegalArgumentException(target + " is not a directory"); + try { + Files.createDirectories(target); + Files.walkFileTree(source, new SimpleFileVisitor() { + @Override + public FileVisitResult postVisitDirectory(Path directory, IOException e) throws IOException { + if (e != null) + throw e; + Path relativePath = source.relativize(directory); + Path targetDirectory = target.resolve(relativePath); + Files.createDirectory(targetDirectory); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Path relativePath = source.relativize(file); + Path targetFile = target.resolve(relativePath); + Files.copy(file, targetFile); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + throw new RuntimeException("Cannot copy " + source + " to " + target, e); + } + + } + /** * Deletes this path, recursively if needed. Does nothing if the path does not * exist. diff --git a/org.argeo.util/src/org/argeo/util/StreamUtils.java b/org.argeo.util/src/org/argeo/util/StreamUtils.java index 6d7d940ce..30404f1e4 100644 --- a/org.argeo.util/src/org/argeo/util/StreamUtils.java +++ b/org.argeo.util/src/org/argeo/util/StreamUtils.java @@ -1,13 +1,15 @@ package org.argeo.util; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; +import java.util.StringJoiner; -/** Utilities to be used when Apache Commons IO is not available. */ -class StreamUtils { +/** Stream utilities to be used when Apache Commons IO is not available. */ +public class StreamUtils { private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; /* @@ -15,8 +17,7 @@ class StreamUtils { */ /** @return the number of bytes */ - public static Long copy(InputStream in, OutputStream out) - throws IOException { + public static Long copy(InputStream in, OutputStream out) throws IOException { Long count = 0l; byte[] buf = new byte[DEFAULT_BUFFER_SIZE]; while (true) { @@ -78,4 +79,12 @@ class StreamUtils { // } } + + public static String toString(BufferedReader reader) throws IOException { + StringJoiner sn = new StringJoiner("\n"); + String line = null; + while ((line = reader.readLine()) != null) + sn.add(line); + return sn.toString(); + } } diff --git a/org.argeo.util/src/org/argeo/util/directory/DirectoryConf.java b/org.argeo.util/src/org/argeo/util/directory/DirectoryConf.java index c0f96ee75..4450ca474 100644 --- a/org.argeo.util/src/org/argeo/util/directory/DirectoryConf.java +++ b/org.argeo.util/src/org/argeo/util/directory/DirectoryConf.java @@ -15,10 +15,10 @@ import org.argeo.util.naming.NamingUtils; /** Properties used to configure user admins. */ public enum DirectoryConf { /** Base DN (cannot be configured externally) */ - baseDn("dc=example,dc=com"), + baseDn(null), /** URI of the underlying resource (cannot be configured externally) */ - uri("ldap://localhost:10389"), + uri(null), /** User objectClass */ userObjectClass("inetOrgPerson"), diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java index 71a87887b..36047d53e 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/AbstractLdapDirectory.java @@ -66,7 +66,11 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv String key = keys.nextElement(); configProperties.put(key, props.get(key)); } - baseDn = toLdapName(DirectoryConf.baseDn.getValue(configProperties)); + + String baseDnStr = DirectoryConf.baseDn.getValue(configProperties); + if (baseDnStr == null) + throw new IllegalArgumentException("Base DN must be specified: " + configProperties); + baseDn = toLdapName(baseDnStr); this.scoped = scoped; if (uriArg != null) { @@ -119,19 +123,26 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv // TODO manage generic redundant LDAP case directoryDao = new LdapDao(this); } else { - URI u = URI.create(uri); - if (DirectoryConf.SCHEME_LDAP.equals(u.getScheme()) || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) { - directoryDao = new LdapDao(this); - } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) { - directoryDao = new LdifDao(this); - } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) { - directoryDao = new OsUserDirectory(this); - // singleUser = true; + if (uri != null) { + URI u = URI.create(uri); + if (DirectoryConf.SCHEME_LDAP.equals(u.getScheme()) + || DirectoryConf.SCHEME_LDAPS.equals(u.getScheme())) { + directoryDao = new LdapDao(this); + } else if (DirectoryConf.SCHEME_FILE.equals(u.getScheme())) { + directoryDao = new LdifDao(this); + } else if (DirectoryConf.SCHEME_OS.equals(u.getScheme())) { + directoryDao = new OsUserDirectory(this); + // singleUser = true; + } else { + throw new IllegalArgumentException("Unsupported scheme " + u.getScheme()); + } } else { - throw new IllegalArgumentException("Unsupported scheme " + u.getScheme()); + // in memory + directoryDao = new LdifDao(this); } } - xaResource = new WorkingCopyXaResource<>(directoryDao); + if (directoryDao != null) + xaResource = new WorkingCopyXaResource<>(directoryDao); } /* @@ -256,11 +267,12 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv LdapEntry group = doGetRole(groupDn); if (group != null) { allRoles.add(group); - }else { + } else { // user doesn't have the right to retrieve role, but we know it exists // otherwise memberOf would not work Attributes a = new BasicAttributes(); - a.put(LdapNameUtils.getLastRdn(groupDn).getType(), LdapNameUtils.getLastRdn(groupDn).getValue()); + a.put(LdapNameUtils.getLastRdn(groupDn).getType(), + LdapNameUtils.getLastRdn(groupDn).getValue()); a.put(LdapAttrs.objectClass.name(), LdapObjs.groupOfNames.name()); group = newGroup(groupDn, a); allRoles.add(group); @@ -378,7 +390,7 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv protected boolean isExternal(LdapName name) { return !name.startsWith(baseDn); } - + protected static boolean hasObjectClass(Attributes attrs, LdapObjs objectClass) { return hasObjectClass(attrs, objectClass.name()); } diff --git a/org.argeo.util/src/org/argeo/util/directory/ldap/LdifDao.java b/org.argeo.util/src/org/argeo/util/directory/ldap/LdifDao.java index 7387d9e0f..d74ac166f 100644 --- a/org.argeo.util/src/org/argeo/util/directory/ldap/LdifDao.java +++ b/org.argeo.util/src/org/argeo/util/directory/ldap/LdifDao.java @@ -35,65 +35,20 @@ import org.osgi.service.useradmin.Role; /** A user admin based on a LDIF files. */ public class LdifDao extends AbstractLdapDirectoryDao { -// private NavigableMap users = new TreeMap<>(); -// private NavigableMap groups = new TreeMap<>(); private NavigableMap entries = new TreeMap<>(); private NavigableMap hierarchy = new TreeMap<>(); -// private List rootHierarchyUnits = new ArrayList<>(); - -// public LdifUserAdmin(String uri, String baseDn) { -// this(fromUri(uri, baseDn), false); -// } public LdifDao(AbstractLdapDirectory directory) { super(directory); } -// protected LdifUserAdmin(Hashtable properties, boolean scoped) { -// super( properties, scoped); -// } - -// public LdifUserAdmin(URI uri, Dictionary properties) { -// super(uri, properties, false); -// } - -// @Override -// protected AbstractUserDirectory scope(User user) { -// Dictionary credentials = user.getCredentials(); -// String username = (String) credentials.get(SHARED_STATE_USERNAME); -// if (username == null) -// username = user.getName(); -// Object pwdCred = credentials.get(SHARED_STATE_PASSWORD); -// byte[] pwd = (byte[]) pwdCred; -// if (pwd != null) { -// char[] password = DirectoryDigestUtils.bytesToChars(pwd); -// User directoryUser = (User) getRole(username); -// if (!directoryUser.hasCredential(null, password)) -// throw new IllegalStateException("Invalid credentials"); -// } else { -// throw new IllegalStateException("Password is required"); -// } -// Dictionary properties = cloneProperties(); -// properties.put(DirectoryConf.readOnly.name(), "true"); -// LdifUserAdmin scopedUserAdmin = new LdifUserAdmin(properties, true); -//// scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups); -//// scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users); -// scopedUserAdmin.entries = Collections.unmodifiableNavigableMap(entries); -// return scopedUserAdmin; -// } - -// private static Dictionary fromUri(String uri, String baseDn) { -// Hashtable res = new Hashtable(); -// res.put(DirectoryConf.uri.name(), uri); -// res.put(DirectoryConf.baseDn.name(), baseDn); -// return res; -// } - public void init() { - + String uri = getDirectory().getUri(); + if (uri == null) + return; try { - URI u = new URI(getDirectory().getUri()); + URI u = new URI(uri); if (u.getScheme().equals("file")) { File file = new File(u); if (!file.exists()) @@ -107,7 +62,7 @@ public class LdifDao extends AbstractLdapDirectoryDao { public void save() { if (getDirectory().getUri() == null) - throw new IllegalStateException("Cannot save LDIF user admin: no URI is set"); + return; // ignore if (getDirectory().isReadOnly()) throw new IllegalStateException( "Cannot save LDIF user admin: " + getDirectory().getUri() + " is read-only"); @@ -123,10 +78,6 @@ public class LdifDao extends AbstractLdapDirectoryDao { LdifWriter ldifWriter = new LdifWriter(out); for (LdapName name : hierarchy.keySet()) ldifWriter.writeEntry(name, hierarchy.get(name).getAttributes()); -// for (LdapName name : groups.keySet()) -// ldifWriter.writeEntry(name, groups.get(name).getAttributes()); -// for (LdapName name : users.keySet()) -// ldifWriter.writeEntry(name, users.get(name).getAttributes()); for (LdapName name : entries.keySet()) ldifWriter.writeEntry(name, entries.get(name).getAttributes()); } finally { @@ -136,8 +87,6 @@ public class LdifDao extends AbstractLdapDirectoryDao { public void load(InputStream in) { try { -// users.clear(); -// groups.clear(); entries.clear(); hierarchy.clear(); @@ -167,14 +116,7 @@ public class LdifDao extends AbstractLdapDirectoryDao { } else if (objectClass.toLowerCase().equals(getDirectory().getGroupObjectClass().toLowerCase())) { entries.put(key, newGroup(key, attributes)); break objectClasses; -// } else if (objectClass.equalsIgnoreCase(LdapObjs.organization.name())) { -// // we only consider organizations which are not groups -// hierarchy.put(key, new LdifHierarchyUnit(this, key, HierarchyUnit.ORGANIZATION, attributes)); -// break objectClasses; } else if (objectClass.equalsIgnoreCase(LdapObjs.organizationalUnit.name())) { -// String name = key.getRdn(key.size() - 1).toStrindirectoryDaog(); -// if (getUserBase().equalsIgnoreCase(name) || getGroupBase().equalsIgnoreCase(name)) -// break objectClasses; // skip // TODO skip if it does not contain groups or users hierarchy.put(key, new LdapHierarchyUnit(getDirectory(), key, attributes)); break objectClasses; @@ -182,19 +124,6 @@ public class LdifDao extends AbstractLdapDirectoryDao { } } - // link hierarchy -// hierachyUnits: for (LdapName dn : hierarchy.keySet()) { -// LdifHierarchyUnit unit = hierarchy.get(dn); -// LdapName parentDn = (LdapName) dn.getPrefix(dn.size() - 1); -// LdifHierarchyUnit parent = hierarchy.get(parentDn); -// if (parent == null) { -// rootHierarchyUnits.add(unit); -// unit.parent = null; -// continue hierachyUnits; -// } -// parent.children.add(unit); -// unit.parent = parent; -// } } catch (NamingException | IOException e) { throw new IllegalStateException("Cannot load user admin service from LDIF", e); } @@ -215,10 +144,6 @@ public class LdifDao extends AbstractLdapDirectoryDao { @Override public LdapEntry doGetEntry(LdapName key) throws NameNotFoundException { -// if (groups.containsKey(key)) -// return groups.get(key); -// if (users.containsKey(key)) -// return users.get(key); if (entries.containsKey(key)) return entries.get(key); throw new NameNotFoundException(key + " not persisted"); @@ -243,12 +168,8 @@ public class LdifDao extends AbstractLdapDirectoryDao { Objects.requireNonNull(searchBase); ArrayList res = new ArrayList<>(); if (f == null && deep && getDirectory().getBaseDn().equals(searchBase)) { -// res.addAll(users.values()); -// res.addAll(groups.values()); res.addAll(entries.values()); } else { -// filterRoles(users, searchBase, f, deep, res); -// filterRoles(groups, searchBase, f, deep, res); filterRoles(entries, searchBase, f, deep, res); } return res; @@ -309,26 +230,15 @@ public class LdifDao extends AbstractLdapDirectoryDao { for (LdapName dn : wc.getDeletedData().keySet()) { if (entries.containsKey(dn)) entries.remove(dn); -// if (users.containsKey(dn)) -// users.remove(dn); -// else if (groups.containsKey(dn)) -// groups.remove(dn); else throw new IllegalStateException("User to delete not found " + dn); } // add for (LdapName dn : wc.getNewData().keySet()) { LdapEntry user = (LdapEntry) wc.getNewData().get(dn); -// if (users.containsKey(dn) || groups.containsKey(dn)) if (entries.containsKey(dn)) throw new IllegalStateException("User to create found " + dn); entries.put(dn, user); -// else if (Role.USER == user.getType()) -// users.put(dn, user); -// else if (Role.GROUP == user.getType()) -// groups.put(dn, (DirectoryGroup) user); -// else -// throw new IllegalStateException("Unsupported role type " + user.getType() + " for new user " + dn); } // modify for (LdapName dn : wc.getModifiedData().keySet()) { @@ -358,16 +268,6 @@ public class LdifDao extends AbstractLdapDirectoryDao { /* * HIERARCHY */ - -// @Override -// public int getHierarchyChildCount() { -// return rootHierarchyUnits.size(); -// } -// -// @Override -// public HierarchyUnit getHierarchyChild(int i) { -// return rootHierarchyUnits.get(i); -// } @Override public HierarchyUnit doGetHierarchyUnit(LdapName dn) { if (getDirectory().getBaseDn().equals(dn)) @@ -397,20 +297,4 @@ public class LdifDao extends AbstractLdapDirectoryDao { public void scope(LdifDao scoped) { scoped.entries = Collections.unmodifiableNavigableMap(entries); } - -// @Override -// public Iterable getDirectHierarchyUnits(boolean functionalOnly) { -// if (functionalOnly) { -// List res = new ArrayList<>(); -// for (HierarchyUnit hu : rootHierarchyUnits) { -// if (hu.isFunctional()) -// res.add(hu); -// } -// return res; -// -// } else { -// return rootHierarchyUnits; -// } -// } - }