--- /dev/null
+category: org.argeo.commons
+version: 2.1.104
+buildId: S
+#buildId: r${tstamp}
+
+Bundle-Version: 2.1.104.S
+Bundle-RequiredExecutionEnvironment: JavaSE-11
+
+Private-Package: org.argeo.*.internal.*
+Export-Package: !org.argeo.*.internal.*; org.argeo.*
+SLC-Category: ${category}
+#SLC-Build-Timestamp: ${tstamp}
+-savemanifest = META-INF/MANIFEST.MF
+-removeheaders = Bnd-LastModified,Build-Jdk,Built-By,Tool,Created-By
+-groupId = ${category}
+Automatic-Module-Name: ${bsn}
+
+
# Local
org.osgi.service.http.port=7070
-argeo.node.useradmin.uris=ldap://cn=Directory%20Manager:argeoargeo@test-pgsql-ldap:10389/dc=example,dc=com
+argeo.node.useradmin.uris=ldap://cn=Directory%20Manager:argeoargeo@test-pgsql-ldap/dc=example,dc=com
argeo.node.repo.type=postgresql_cluster_ds
argeo.node.repo.clusterId=03233754-16c3-49a1-8a00-58bf89a65182
argeo.node.repo.dburl=jdbc:postgresql://test-pgsql-ldap/argeo_cluster
# Local
org.osgi.service.http.port=7071
-argeo.node.useradmin.uris=ldap://cn=Directory%20Manager:argeoargeo@test-pgsql-ldap:10389/dc=example,dc=com
+argeo.node.useradmin.uris=ldap://cn=Directory%20Manager:argeoargeo@test-pgsql-ldap/dc=example,dc=com
argeo.node.repo.type=postgresql_cluster_ds
argeo.node.repo.clusterId=52463fa3-2917-4814-9ff7-685c41cbc7c7
argeo.node.repo.dburl=jdbc:postgresql://test-pgsql-ldap/argeo_cluster
<dependencies>
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.dep.cms.sdk</artifactId>
+ <artifactId>org.argeo.dep.cms.e4.rap</artifactId>
<version>2.3.1-SNAPSHOT</version>
</dependency>
</dependencies>
--- /dev/null
+/target/
+/feature.xml
+/modularDistribution.csv
+/*-maven.target
--- /dev/null
+/MANIFEST.MF
--- /dev/null
+bin.includes = feature.xml,\
+ modularDistribution.csv
--- /dev/null
+properties.1.name=org.eclipse.equinox.p2.type.category
+properties.1.value=true
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.argeo.commons</groupId>
+ <version>2.3.1-SNAPSHOT</version>
+ <artifactId>dep</artifactId>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>org.argeo.dep.cms.ext</artifactId>
+ <name>CMS Optional Third Parties</name>
+ <description>Bulky generic third parties which are not required by the CMS, but necessary for upper layers.</description>
+ <dependencies>
+ <!-- Additional Third Parties -->
+ <dependency>
+ <groupId>org.argeo.tp.javax</groupId>
+ <artifactId>javax.xml.bind</artifactId>
+ </dependency>
+
+ <!-- Jackson JSON processor -->
+ <dependency>
+ <groupId>org.argeo.tp.jackson</groupId>
+ <artifactId>com.fasterxml.jackson.core.jackson-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.jackson</groupId>
+ <artifactId>com.fasterxml.jackson.core.jackson-databind</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.jackson</groupId>
+ <artifactId>com.fasterxml.jackson.core.jackson-annotations</artifactId>
+ </dependency>
+
+ <!-- Mail -->
+ <dependency>
+ <groupId>org.argeo.tp.javax</groupId>
+ <artifactId>javax.activation</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.javax</groupId>
+ <artifactId>javax.mail</artifactId>
+ </dependency>
+
+ <!-- POI requirements -->
+ <dependency>
+ <groupId>org.argeo.tp.apache.commons</groupId>
+ <artifactId>org.apache.commons.math3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.apache.commons</groupId>
+ <artifactId>org.apache.commons.collections4</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.apache</groupId>
+ <artifactId>org.apache.xml.security</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.apache</groupId>
+ <artifactId>org.apache.xmlbeans</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.apache</groupId>
+ <artifactId>org.apache.xalan</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.apache</groupId>
+ <artifactId>org.apache.xalan.serializer</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.apache</groupId>
+ <artifactId>org.apache.xml.resolver</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.argeo.tp.apache</groupId>
+ <artifactId>org.apache.xerces</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>rpmbuild-tp</id>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>prepare-source-tp</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptorRefs>
+ <descriptorRef>a2-source-tp</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>rpm-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>rpm-tp</id>
+ <phase>package</phase>
+ <goals>
+ <goal>rpm</goal>
+ </goals>
+ <configuration>
+ <name>argeo-cms-ext-tp</name>
+ <projversion>${version.argeo-tp}</projversion>
+ <release>${maven.build.timestamp}</release>
+ <mappings>
+ <mapping>
+ <directory>/usr/share/osgi</directory>
+ <username>root</username>
+ <groupname>root</groupname>
+ <filemode>644</filemode>
+ <directoryIncluded>false</directoryIncluded>
+ <sources>
+ <source>
+ <location>${project.build.directory}/${project.artifactId}-${project.version}-a2-source-tp</location>
+ <includes>
+ <include>**/*.jar</include>
+ </includes>
+ </source>
+ </sources>
+ </mapping>
+ </mappings>
+ <requires>
+ <require>argeo-cms-node-tp</require>
+ </requires>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
\ No newline at end of file
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.argeo.commons</groupId>
- <version>2.3.1-SNAPSHOT</version>
+ <version>2.1.104-SNAPSHOT</version>
<artifactId>dep</artifactId>
<relativePath>..</relativePath>
</parent>
<!-- Parent dependencies -->
<dependency>
<groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.dep.cms.e4.rap</artifactId>
- <version>2.3.1-SNAPSHOT</version>
+ <artifactId>org.argeo.dep.cms.ui.rap</artifactId>
+ <version>2.1.104-SNAPSHOT</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>org.argeo.osgi.boot</artifactId>
- <version>2.3.1-SNAPSHOT</version>
+ <version>2.1.104-SNAPSHOT</version>
<scope>test</scope>
</dependency>
</mapping>
</mappings>
<requires>
- <require>argeo-cms-platform-tp</require>
</requires>
</configuration>
</execution>
<module>org.argeo.dep.cms.node</module>
<module>org.argeo.dep.cms.ui.rap</module>
<module>org.argeo.dep.cms.e4.rap</module>
- <module>org.argeo.dep.cms.sdk</module>
+ <module>org.argeo.dep.cms.ext</module>
</modules>
<build>
<plugins>
--- /dev/null
+-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=127.0.0.1:8000
\ No newline at end of file
<artifactId>org.argeo.dep.cms.e4.rap</artifactId>
<version>2.3.1-SNAPSHOT</version>
</dependency>
- <dependency>
- <groupId>org.argeo.commons</groupId>
- <artifactId>org.argeo.dep.cms.sdk</artifactId>
- <version>2.3.1-SNAPSHOT</version>
- </dependency>
<dependency>
<groupId>org.argeo.commons</groupId>
<artifactId>osgi-boot</artifactId>
-Dosgi.noShutdown=true \
-Dorg.eclipse.equinox.http.jetty.autostart=false \
-Dosgi.bundles=org.argeo.osgi.boot@start \
-@/usr/share/osgi/boot/framework.args \
@/usr/share/argeo/jvm.args \
@/etc/argeo.d/jvm.args \
@/etc/argeo.d/%I/jvm.args \
+@/usr/share/osgi/boot/framework.args \
-configuration /var/lib/argeo.d/%I/state \
-data /var/lib/argeo.d/%I/data
# Exit codes of the JVM when SIGTERM or SIGINT have been caught:
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
--- /dev/null
+package org.argeo.api;
+
+import org.osgi.resource.Namespace;
+
+/** Namespace defining which resources can be published. Typically use to expose icon of scripts to the web. */
+public class PublishNamespace extends Namespace {
+
+ public static final String CMS_PUBLISH_NAMESPACE = "cms.publish";
+ public static final String PKG = "pkg";
+ public static final String FILE = "file";
+
+ private PublishNamespace() {
+ // empty
+ }
+
+}
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.util.LangUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.Application.OperationMode;
import org.eclipse.rap.rwt.application.ApplicationConfiguration;
import org.eclipse.rap.rwt.application.ExceptionHandler;
-import org.eclipse.rap.rwt.application.Application.OperationMode;
import org.eclipse.rap.rwt.client.WebClient;
import org.eclipse.swt.widgets.Display;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.event.EventAdmin;
private BundleContext bundleContext;
private CmsApp cmsApp;
+ private String cmsAppId;
private EventAdmin eventAdmin;
private ServiceRegistration<ApplicationConfiguration> rwtAppReg;
private final static String CONTEXT_NAME = "contextName";
private String contextName;
+ private final static String FAVICON_PNG = "favicon.png";
+
public void init(BundleContext bundleContext, Map<String, String> properties) {
this.bundleContext = bundleContext;
contextName = properties.get(CONTEXT_NAME);
- if (cmsApp != null)
- themingUpdated();
+ if (cmsApp != null) {
+ if (cmsApp.allThemesAvailable())
+ publishWebApp();
+ }
}
public void destroy(BundleContext bundleContext, Map<String, String> properties) {
- if (cmsApp != null)
+ if (cmsApp != null) {
cmsApp.removeCmsAppListener(this);
+ cmsApp = null;
+ }
}
@Override
if (theme != null) {
properties.put(WebClient.THEME_ID, theme.getThemeId());
properties.put(WebClient.HEAD_HTML, theme.getHtmlHeaders());
+ properties.put(WebClient.BODY_HTML, theme.getBodyHtml());
+ Set<String> imagePaths = theme.getImagesPaths();
+ if (imagePaths.contains(FAVICON_PNG)) {
+ properties.put(WebClient.FAVICON, FAVICON_PNG);
+ }
} else {
properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID);
}
if (log.isDebugEnabled())
log.info("Added web entry point " + (contextName != null ? "/" + contextName : "") + entryPointName);
}
- if (log.isDebugEnabled())
- log.debug("Published CMS web app /" + (contextName != null ? contextName : ""));
+// if (log.isDebugEnabled())
+// log.debug("Published CMS web app /" + (contextName != null ? contextName : ""));
}
CmsApp getCmsApp() {
public void setCmsApp(CmsApp cmsApp, Map<String, String> properties) {
this.cmsApp = cmsApp;
+ this.cmsAppId = properties.get(Constants.SERVICE_PID);
this.cmsApp.addCmsAppListener(this);
}
public void unsetCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+ String cmsAppId = properties.get(Constants.SERVICE_PID);
+ if (!cmsAppId.equals(this.cmsAppId))
+ return;
+ if (this.cmsApp != null) {
+ this.cmsApp.removeCmsAppListener(this);
+ }
if (rwtAppReg != null)
rwtAppReg.unregister();
this.cmsApp = null;
@Override
public void themingUpdated() {
+ if (cmsApp != null && cmsApp.allThemesAvailable())
+ publishWebApp();
+ }
+
+ protected void publishWebApp() {
Dictionary<String, Object> regProps = LangUtils.dict(CONTEXT_NAME, contextName);
if (rwtAppReg != null)
rwtAppReg.unregister();
Locale rwtLocale = RWT.getUISession().getLocale();
LocaleUtils.setThreadLocale(rwtLocale);
}
+ parent.setData(CmsApp.UI_NAME_PROPERTY, uiName);
ui = cmsWebApp.getCmsApp().initUi(parent);
- ui.setData(CmsApp.UI_NAME_PROPERTY, uiName);
ui.setLayoutData(CmsUiUtils.fillAll());
// we need ui to be set before refresh so that CmsView can store UI context data
// in it.
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
String themeId = getThemeId(uiName);
if (themeId == null)
return null;
+ if (!themes.containsKey(themeId))
+ throw new IllegalArgumentException("Theme " + themeId + " not found.");
return themes.get(themeId);
}
- protected boolean allThemesAvailable() {
+ @Override
+ public boolean allThemesAvailable() {
boolean themeMissing = false;
uiNames: for (String uiName : getUiNames()) {
String themeId = getThemeId(uiName);
CmsTheme getTheme(String uiName);
+ boolean allThemesAvailable();
+
void addCmsAppListener(CmsAppListener listener);
void removeCmsAppListener(CmsAppListener listener);
package org.argeo.cms.ui;
/** Styles references in the CSS. */
+@Deprecated
public interface CmsStyles {
// General
public final static String CMS_SHELL = "cms_shell";
/** Tags to be added to the header section of the HTML page. */
String getHtmlHeaders();
+ /** The HTML body to use. */
+ String getBodyHtml();
+
/** The image registered at this path, or <code>null</code> if not found. */
Image getImage(String path);
/** Centralises some generic {@link CmsTheme} patterns. */
public abstract class AbstractCmsTheme implements CmsTheme {
- private Map<String, Image> imageCache = new HashMap<>();
+ private Map<String, ImageData> imageCache = new HashMap<>();
private Map<String, Map<Integer, String>> iconPaths = new HashMap<>();
if (in == null)
return null;
ImageData imageData = new ImageData(in);
- Image image = new Image(Display.getDefault(), imageData);
- imageCache.put(path, image);
+ imageCache.put(path, imageData);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
- return imageCache.get(path);
+ ImageData imageData = imageCache.get(path);
+ Image image = new Image(Display.getCurrent(), imageData);
+ return image;
}
@Override
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.List;
import java.util.TreeSet;
import java.util.stream.Collectors;
+import org.apache.commons.io.IOUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
public final static String CMS_THEME_PROPERTY = "argeo.cms.theme";
public final static String CMS_THEME_BUNDLE_PROPERTY = "argeo.cms.theme.bundle";
+ private final static String HEADER_CSS = "header.css";
+ private final static String FONTS_TXT = "fonts.txt";
+ private final static String BODY_HTML = "body.html";
+
// private final static Log log = LogFactory.getLog(BundleCmsTheme.class);
private String themeId;
private String headerCss;
private List<String> fonts = new ArrayList<>();
+ private String bodyHtml="<body></body>";
+
private String basePath;
private String styleCssPath;
// private String webCssPath;
addFonts("*.woff2");
// fonts
- URL fontsUrl = themeBundle.getEntry(basePath + "fonts.txt");
+ URL fontsUrl = themeBundle.getEntry(basePath + FONTS_TXT);
if (fontsUrl != null) {
loadFontsUrl(fontsUrl);
}
// common CSS header (plain CSS)
- URL headerCssUrl = themeBundle.getEntry(basePath + "header.css");
+ URL headerCssUrl = themeBundle.getEntry(basePath + HEADER_CSS);
if (headerCssUrl != null) {
+ // added to plain Web CSS
+ webCssPaths.add(basePath + HEADER_CSS);
+ // and it will also be used by RAP:
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(headerCssUrl.openStream(), UTF_8))) {
headerCss = buffer.lines().collect(Collectors.joining("\n"));
} catch (IOException e) {
throw new IllegalArgumentException("Cannot read " + headerCssUrl, e);
}
}
- }
+
+ // body
+ URL bodyUrl = themeBundle.getEntry(basePath + BODY_HTML);
+ if (bodyUrl != null) {
+ loadBodyHtml(bodyUrl);
+ }
+}
public String getHtmlHeaders() {
StringBuilder sb = new StringBuilder();
else
return sb.toString();
}
+
+
+
+ @Override
+ public String getBodyHtml() {
+ return bodyHtml;
+ }
Set<String> addCss(Bundle themeBundle, String path) {
Set<String> paths = new TreeSet<>();
}
}
+ void loadBodyHtml(URL url) {
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) {
+ bodyHtml= IOUtils.toString(url,StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Cannot load URL " + url, e);
+ }
+ }
+
void addImages(String pattern) {
Enumeration<URL> themeResources = themeBundle.findEntries(basePath, pattern, true);
if (themeResources == null)
package org.argeo.cms.ui.util;
+import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
-import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.api.NodeUtils;
-import org.argeo.cms.CmsException;
import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.ui.CmsStyles;
import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.jcr.JcrException;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.service.ResourceManager;
import org.eclipse.swt.SWT;
private BundleContext bundleContext;
private String label;
- private String custom;
+ private String style;
private String target;
private String image;
+ private boolean openNew = false;
private MouseListener mouseListener;
private int horizontalAlignment = SWT.CENTER;
}
public CmsLink(String label, String target) {
- this(label, target, null);
+ this(label, target, (String) null);
}
- public CmsLink(String label, String target, String custom) {
+ public CmsLink(String label, String target, CmsStyle style) {
+ this(label, target, style != null ? style.style() : null);
+ }
+
+ public CmsLink(String label, String target, String style) {
super();
this.label = label;
this.target = target;
- this.custom = custom;
+ this.style = style;
init();
}
comp.setLayout(CmsUiUtils.noSpaceGridLayout());
Label link = new Label(comp, SWT.NONE);
- link.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
+ CmsUiUtils.markup(link);
GridData layoutData = new GridData(horizontalAlignment, verticalAlignment, false, false);
if (image != null) {
if (imageHeight != null)
}
link.setLayoutData(layoutData);
- if (custom != null) {
- comp.setData(RWT.CUSTOM_VARIANT, custom);
- link.setData(RWT.CUSTOM_VARIANT, custom);
- } else {
- comp.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK);
- link.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK);
- }
+ CmsUiUtils.style(comp, style != null ? style : getDefaultStyle());
+ CmsUiUtils.style(link, style != null ? style : getDefaultStyle());
// label
StringBuilder labelText = new StringBuilder();
String homePath = homeNode.getPath();
labelText.append("/#" + homePath);
} catch (RepositoryException e) {
- throw new CmsException("Cannot get home path", e);
+ throw new JcrException("Cannot get home path", e);
}
} else {
labelText.append(loggedInTarget);
}
labelText.append("\">");
} else if (target != null) {
- labelText.append("<a style='color:inherit;text-decoration:inherit;' href=\"");
- labelText.append(target);
- labelText.append("\">");
+ labelText.append("<a style='color:inherit;text-decoration:inherit;' href='");
+ labelText.append(target).append("'");
+ if (openNew) {
+ labelText.append(" target='_blank'");
+ }
+ labelText.append(">");
}
if (image != null) {
registerImageIfNeeded();
ResourceManager resourceManager = RWT.getResourceManager();
if (!resourceManager.isRegistered(image)) {
URL res = getImageUrl();
- InputStream inputStream = null;
- try {
- IOUtils.closeQuietly(inputStream);
- inputStream = res.openStream();
+ try (InputStream inputStream = res.openStream()) {
resourceManager.register(image, inputStream);
if (log.isTraceEnabled())
log.trace("Registered image " + image);
- } catch (Exception e) {
- throw new CmsException("Cannot load image " + image, e);
- } finally {
- IOUtils.closeQuietly(inputStream);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot load image " + image, e);
}
}
}
private ImageData loadImage() {
URL url = getImageUrl();
ImageData result = null;
- InputStream inputStream = null;
- try {
- inputStream = url.openStream();
+ try (InputStream inputStream = url.openStream()) {
result = new ImageData(inputStream);
if (log.isTraceEnabled())
log.trace("Loaded image " + image);
- } catch (Exception e) {
- throw new CmsException("Cannot load image " + image, e);
- } finally {
- IOUtils.closeQuietly(inputStream);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot load image " + image, e);
}
return result;
}
}
if (url == null)
- throw new CmsException("No image " + image + " available.");
+ throw new IllegalStateException("No image " + image + " available.");
return url;
}
this.label = label;
}
+ public void setStyle(String style) {
+ this.style = style;
+ }
+
+ /** @deprecated Use {@link #setStyle(String)} instead. */
+ @Deprecated
public void setCustom(String custom) {
- this.custom = custom;
+ this.style = custom;
}
public void setTarget(String target) {
} else if ("center".equals(vAlign)) {
verticalAlignment = SWT.CENTER;
} else {
- throw new CmsException("Unsupported vertical allignment " + vAlign + " (must be: top, bottom or center)");
+ throw new IllegalArgumentException(
+ "Unsupported vertical alignment " + vAlign + " (must be: top, bottom or center)");
}
}
this.imageHeight = imageHeight;
}
+ public void setOpenNew(boolean openNew) {
+ this.openNew = openNew;
+ }
+
+ protected String getDefaultStyle() {
+ return SimpleStyle.link.name();
+ }
}
public interface CmsStyle {
String name();
+ /** @deprecated use {@link #style()} instead. */
@Deprecated
default String toStyleClass() {
- return getClassPrefix() + "-" + name();
+ return style();
}
default String style() {
- return getClassPrefix() + "-" + name();
+ String classPrefix = getClassPrefix();
+ return "".equals(classPrefix) ? name() : classPrefix + "-" + name();
}
default String getClassPrefix() {
- return "cms";
+ return "";
}
}
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
+import java.util.StringTokenizer;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import org.argeo.cms.ui.CmsView;
import org.argeo.eclipse.ui.Selected;
import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
+import org.argeo.jcr.JcrException;
import org.argeo.jcr.JcrUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.service.ResourceManager;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowData;
+import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
return NodeUtils.getDataPath(cn, node);
}
+ /** Clean reserved URL characters for use in HTTP links. */
+ public static String getDataPathForUrl(Node node) throws RepositoryException {
+ return cleanPathForUrl(getDataPath(node));
+ }
+
+ /** Clean reserved URL characters for use in HTTP links. */
+ public static String cleanPathForUrl(String path) throws RepositoryException {
+ StringTokenizer st = new StringTokenizer(path, "/");
+ StringBuilder sb = new StringBuilder();
+ while (st.hasMoreElements()) {
+ sb.append('/');
+ String encoded = URLEncoder.encode(st.nextToken(), StandardCharsets.UTF_8);
+ encoded = encoded.replace("+", "%20");
+ sb.append(encoded);
+
+ }
+ return sb.toString();
+ }
+
/** @deprecated Use rowData16px() instead. GridData should not be reused. */
@Deprecated
public static RowData ROW_DATA_16px = new RowData(16, 16);
return noSpaceGridLayout(new GridLayout(columns, false));
}
+ /** @return the same layout, with spaces removed. */
public static GridLayout noSpaceGridLayout(GridLayout layout) {
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
/*
* ROW LAYOUT
*/
+ /** @return the same layout, with margins removed. */
+ public static RowLayout noMarginsRowLayout(RowLayout rowLayout) {
+ rowLayout.marginTop = 0;
+ rowLayout.marginBottom = 0;
+ rowLayout.marginLeft = 0;
+ rowLayout.marginRight = 0;
+ return rowLayout;
+ }
+
+ public static RowLayout noMarginsRowLayout(int type) {
+ return noMarginsRowLayout(new RowLayout(type));
+ }
+
public static RowData rowData16px() {
return new RowData(16, 16);
}
return widget;// does nothing
EclipseUiSpecificUtils.setStyleData(widget, style);
if (widget instanceof Control) {
- CmsView.getCmsView((Control) widget).applyStyles(widget);
+ CmsView cmsView = CmsView.getCmsView((Control) widget);
+ if (cmsView != null)
+ cmsView.applyStyles(widget);
}
return widget;
}
return widget;
}
+ /** Disable markup validation. */
+ public static <T extends Widget> T disableMarkupValidation(T widget) {
+ EclipseUiSpecificUtils.setMarkupValidationDisabledData(widget);
+ return widget;
+ }
+
/**
* Apply markup and set text on {@link Label}, {@link Button}, {@link Text}.
*
/** Dispose all children of a Composite */
public static void clear(Composite composite) {
+ if (composite.isDisposed())
+ return;
for (Control child : composite.getChildren())
child.dispose();
}
}
public static String img(String serverBase, Node fileNode, String width, String height) {
- String src = (serverBase != null ? serverBase : "") + NodeUtils.getDataPath(fileNode);
+// String src = (serverBase != null ? serverBase : "") + NodeUtils.getDataPath(fileNode);
+ String src;
+ try {
+ src = (serverBase != null ? serverBase : "") + getDataPathForUrl(fileNode);
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot get URL data path for " + fileNode, e);
+ }
return imgBuilder(src, width, height).append("/>").toString();
}
/** Singleton. */
private CmsUiUtils() {
}
+
}
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
/** @return null if not available */
@Override
public String getImageUrl(Node node) throws RepositoryException {
- return CmsUiUtils.getDataPath(node);
+ return CmsUiUtils.getDataPathForUrl(node);
}
protected String getResourceName(Node node) throws RepositoryException {
--- /dev/null
+package org.argeo.cms.ui.util;
+
+/** Simple styles used by the CMS UI utilities. */
+public enum SimpleStyle implements CmsStyle {
+ link;
+}
mouseListener = null;
refresh(getControl());
// layout(getControl());
- layoutPage();
+ if (!getControl().isDisposed())
+ layoutPage();
} catch (RepositoryException e) {
throw new JcrException("Cannot refresh", e);
}
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="ext/test"/>
<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.argeo.cms">
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.argeo.cms.filesServlet">
<implementation class="org.argeo.cms.internal.http.CmsWebDavServlet"/>
<service>
<provide interface="javax.servlet.Servlet"/>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.argeo.cms.pkgServlet">
+ <implementation class="org.argeo.cms.internal.http.PkgServlet"/>
+ <service>
+ <provide interface="javax.servlet.Servlet"/>
+ </service>
+ <property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/*"/>
+ <property name="osgi.http.whiteboard.context.select" type="String" value="(osgi.http.whiteboard.context.name=pkgServletContext)"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="org.argeo.cms.pkgServletContext">
+ <implementation class="org.argeo.cms.servlet.CmsServletContext"/>
+ <service>
+ <provide interface="org.osgi.service.http.context.ServletContextHelper"/>
+ </service>
+ <property name="osgi.http.whiteboard.context.name" type="String" value="pkgServletContext"/>
+ <property name="osgi.http.whiteboard.context.path" type="String" value="/pkg"/>
+</scr:component>
org.apache.jackrabbit.webdav.jcr,\
org.apache.commons.httpclient.cookie;resolution:=optional,\
!com.sun.security.jgss,\
+org.osgi.framework.namespace;version=0.0.0,\
org.osgi.*;version=0.0.0,\
org.osgi.service.http.whiteboard,\
*
Service-Component:\
OSGI-INF/cmsUserManager.xml,\
+OSGI-INF/pkgServletContext.xml,\
+OSGI-INF/pkgServlet.xml,\
OSGI-INF/jcrServletContext.xml,\
OSGI-INF/dataServletContext.xml,\
OSGI-INF/filesServletContext.xml,\
import org.argeo.cms.internal.auth.CmsSessionImpl;
import org.argeo.cms.internal.auth.ImpliedByPrincipal;
import org.argeo.cms.internal.http.WebCmsSessionImpl;
-import org.argeo.cms.internal.kernel.Activator;
import org.argeo.osgi.useradmin.AuthenticatingUser;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.http.HttpContext;
import org.osgi.service.useradmin.Authorization;
-/** Centrlaises security related registrations. */
+/** Centralises security related registrations. */
class CmsAuthUtils {
// Standard
final static String SHARED_STATE_NAME = AuthenticatingUser.SHARED_STATE_NAME;
// required for display name:
subject.getPrivateCredentials().add(authorization);
- if (Activator.isSingleUser()) {
- subject.getPrincipals().add(new DataAdminPrincipal());
- }
+ boolean singleUser = authorization instanceof SingleUserAuthorization;
Set<Principal> principals = subject.getPrincipals();
try {
userPrincipal = new X500Principal(name.toString());
principals.add(userPrincipal);
- if (Activator.isSingleUser()) {
+ if (singleUser) {
principals.add(new ImpliedByPrincipal(NodeSecurityUtils.ROLE_ADMIN_NAME, userPrincipal));
+ principals.add(new DataAdminPrincipal());
}
}
// subject.getPrincipals().removeAll(subject.getPrincipals(AnonymousPrincipal.class));
}
+ @SuppressWarnings("unused")
synchronized static void registerSessionAuthorization(HttpServletRequest request, Subject subject,
Authorization authorization, Locale locale) {
// synchronized in order to avoid multiple registrations
HttpSession httpSession = request.getSession(false);
assert httpSession != null;
String httpSessId = httpSession.getId();
- String remoteUser = authorization.getName() != null ? authorization.getName()
- : NodeConstants.ROLE_ANONYMOUS;
+ boolean anonymous = authorization.getName() == null;
+ String remoteUser = !anonymous ? authorization.getName() : NodeConstants.ROLE_ANONYMOUS;
request.setAttribute(HttpContext.REMOTE_USER, remoteUser);
request.setAttribute(HttpContext.AUTHORIZATION, authorization);
- CmsSessionImpl cmsSession = CmsSessionImpl.getByLocalId(httpSessId);
- if (cmsSession != null) {
- if (authorization.getName() != null) {
- if (cmsSession.getAuthorization().getName() == null) {
- cmsSession.close();
- cmsSession = null;
- } else if (!authorization.getName().equals(cmsSession.getAuthorization().getName())) {
+ CmsSessionImpl cmsSession;
+ CmsSessionImpl currentLocalSession = CmsSessionImpl.getByLocalId(httpSessId);
+ if (currentLocalSession != null) {
+ boolean currentLocalSessionAnonymous = currentLocalSession.getAuthorization().getName() == null;
+ if (!anonymous) {
+ if (currentLocalSessionAnonymous) {
+ currentLocalSession.close();
+ // new CMS session
+ cmsSession = new WebCmsSessionImpl(subject, authorization, locale, request);
+ } else if (!authorization.getName().equals(currentLocalSession.getAuthorization().getName())) {
throw new IllegalStateException("Inconsistent user " + authorization.getName()
- + " for existing CMS session " + cmsSession);
- }
- // keyring
- if (cmsSession != null)
+ + " for existing CMS session " + currentLocalSession);
+ } else {
+ // keep current session
+ cmsSession = currentLocalSession;
+ // keyring
subject.getPrivateCredentials().addAll(cmsSession.getSecretKeys());
+ }
} else {// anonymous
- if (cmsSession.getAuthorization().getName() != null) {
- cmsSession.close();
- // TODO rather throw an exception ? log a warning ?
- cmsSession = null;
+ if (!currentLocalSessionAnonymous) {
+ currentLocalSession.close();
+ throw new IllegalStateException(
+ "Existing CMS session " + currentLocalSession + " was not logged out properly.");
}
+ // keep current session
+ cmsSession = currentLocalSession;
}
- } else if (cmsSession == null) {
+ } else {
+ // new CMS session
cmsSession = new WebCmsSessionImpl(subject, authorization, locale, request);
}
- // request.setAttribute(CmsSession.class.getName(), cmsSession);
- if (cmsSession != null) {
- CmsSessionId nodeSessionId = new CmsSessionId(cmsSession.getUuid());
- if (subject.getPrivateCredentials(CmsSessionId.class).size() == 0)
- subject.getPrivateCredentials().add(nodeSessionId);
- else {
- UUID storedSessionId = subject.getPrivateCredentials(CmsSessionId.class).iterator().next()
- .getUuid();
- // if (storedSessionId.equals(httpSessionId.getValue()))
- throw new IllegalStateException(
- "Subject already logged with session " + storedSessionId + " (not " + nodeSessionId + ")");
- }
+
+ if (cmsSession == null)// should be dead code (cf. SuppressWarning of the method)
+ throw new IllegalStateException("CMS session cannot be null");
+
+ CmsSessionId nodeSessionId = new CmsSessionId(cmsSession.getUuid());
+ if (subject.getPrivateCredentials(CmsSessionId.class).size() == 0) {
+ subject.getPrivateCredentials().add(nodeSessionId);
+ } else {
+ UUID storedSessionId = subject.getPrivateCredentials(CmsSessionId.class).iterator().next().getUuid();
+ // if (storedSessionId.equals(httpSessionId.getValue()))
+ throw new IllegalStateException(
+ "Subject already logged with session " + storedSessionId + " (not " + nodeSessionId + ")");
}
} else {
- // TODO desktop, CLI
+ CmsSessionImpl cmsSession = new CmsSessionImpl(subject, authorization, locale, "desktop");
+ CmsSessionId nodeSessionId = new CmsSessionId(cmsSession.getUuid());
+ subject.getPrivateCredentials().add(nodeSessionId);
}
}
* @see SingleUserLoginModule
*/
public class SingleUserAuthorization implements Authorization {
+ private String name;
+
+ public SingleUserAuthorization(String name) {
+ this.name = name;
+ }
@Override
public String getName() {
- return System.getProperty("user.name");
+ return name;
}
@Override
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.security.Principal;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import javax.naming.ldap.LdapName;
import javax.security.auth.Subject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.argeo.api.NodeConstants;
-import org.argeo.api.security.DataAdminPrincipal;
-import org.argeo.cms.internal.auth.ImpliedByPrincipal;
import org.argeo.naming.LdapAttrs;
import org.argeo.osgi.useradmin.IpaUtils;
+import org.argeo.osgi.useradmin.OsUserUtils;
import org.osgi.service.useradmin.Authorization;
/** Login module for when the system is owned by a single user. */
@Override
public boolean commit() throws LoginException {
- X500Principal principal;
+ String authorizationName;
KerberosPrincipal kerberosPrincipal = CmsAuthUtils.getSinglePrincipal(subject, KerberosPrincipal.class);
if (kerberosPrincipal != null) {
LdapName userDn = IpaUtils.kerberosToDn(kerberosPrincipal.getName());
- principal = new X500Principal(userDn.toString());
+ X500Principal principal = new X500Principal(userDn.toString());
+ authorizationName = principal.getName();
} else {
Object username = sharedState.get(CmsAuthUtils.SHARED_STATE_NAME);
if (username == null)
hostname = "localhost";
}
String baseDn = ("." + hostname).replaceAll("\\.", ",dc=");
- principal = new X500Principal(LdapAttrs.uid + "=" + username + baseDn);
+ X500Principal principal = new X500Principal(LdapAttrs.uid + "=" + username + baseDn);
+ authorizationName = principal.getName();
}
- Set<Principal> principals = subject.getPrincipals();
- principals.add(principal);
- principals.add(new ImpliedByPrincipal(NodeConstants.ROLE_ADMIN, principal));
- principals.add(new DataAdminPrincipal());
HttpServletRequest request = (HttpServletRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST);
Locale locale = Locale.getDefault();
locale = request.getLocale();
if (locale == null)
locale = Locale.getDefault();
- Authorization authorization = new SingleUserAuthorization();
+ Authorization authorization = new SingleUserAuthorization(authorizationName);
CmsAuthUtils.addAuthorization(subject, authorization);
+
+ // Add standard Java OS login
+ OsUserUtils.loginAsSystemUser(subject);
+
+ // additional principals (must be after Authorization registration)
+// Set<Principal> principals = subject.getPrincipals();
+// principals.add(principal);
+// principals.add(new ImpliedByPrincipal(NodeConstants.ROLE_ADMIN, principal));
+// principals.add(new DataAdminPrincipal());
+
CmsAuthUtils.registerSessionAuthorization(request, subject, authorization, locale);
return true;
import org.argeo.naming.LdapAttrs;
import org.argeo.osgi.useradmin.AuthenticatingUser;
import org.argeo.osgi.useradmin.IpaUtils;
-import org.argeo.osgi.useradmin.OsUserUtils;
import org.argeo.osgi.useradmin.TokenUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
private Authorization bindAuthorization = null;
- private boolean singleUser = Activator.isSingleUser();
+// private boolean singleUser = Activator.isSingleUser();
@SuppressWarnings("unchecked")
@Override
username = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_NAME);
password = null;
preauth = true;
- } else if (singleUser) {
- username = OsUserUtils.getOsUsername();
- password = null;
- // TODO retrieve from http session
- locale = Locale.getDefault();
+// } else if (singleUser) {
+// username = OsUserUtils.getOsUsername();
+// password = null;
+// // TODO retrieve from http session
+// locale = Locale.getDefault();
} else {
// ask for username and password
// TODO check CRLs/OSCP validity?
// NB: authorization in commit() will work only if an LDAP connection password
// is provided
- } else if (singleUser) {
- // TODO verify IP address?
+// } else if (singleUser) {
+// // TODO verify IP address?
} else if (preauth) {
// ident
} else {
if (locale != null)
subject.getPublicCredentials().add(locale);
- if (singleUser) {
- OsUserUtils.loginAsSystemUser(subject);
- }
+// if (singleUser) {
+// OsUserUtils.loginAsSystemUser(subject);
+// }
UserAdmin userAdmin = Activator.getUserAdmin();
Authorization authorization;
if (callbackHandler == null) {// anonymous
Set<User> collectedUsers = new HashSet<>();
// try dn
User user = null;
+ user = null;
// try all indexes
for (String attr : indexedUserProperties) {
user = userAdmin.getUser(attr, providedUsername);
package org.argeo.cms.internal.auth;
+import java.io.Serializable;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
-import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.osgi.service.useradmin.Authorization;
/** Default CMS session implementation. */
-public class CmsSessionImpl implements CmsSession {
+public class CmsSessionImpl implements CmsSession, Serializable {
+ private static final long serialVersionUID = 1867719354246307225L;
private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionImpl.class).getBundleContext();
private final static Log log = LogFactory.getLog(CmsSessionImpl.class);
// private final Subject initialSubject;
- private final AccessControlContext initialContext;
+ private transient AccessControlContext accessControlContext;
private final UUID uuid;
private final String localSessionId;
- private final Authorization authorization;
+ private Authorization authorization;
private final LdapName userDn;
private final boolean anonymous;
private Map<String, Session> dataSessions = new HashMap<>();
private Set<String> dataSessionsInUse = new HashSet<>();
- private LinkedHashSet<Session> additionalDataSessions = new LinkedHashSet<>();
+ private Set<Session> additionalDataSessions = new HashSet<>();
private Map<String, Object> views = new HashMap<>();
public CmsSessionImpl(Subject initialSubject, Authorization authorization, Locale locale, String localSessionId) {
this.creationTime = ZonedDateTime.now();
this.locale = locale;
- this.initialContext = Subject.doAs(initialSubject, new PrivilegedAction<AccessControlContext>() {
+ this.accessControlContext = Subject.doAs(initialSubject, new PrivilegedAction<AccessControlContext>() {
@Override
public AccessControlContext run() {
lc.logout();
} catch (LoginException e) {
log.warn("Could not logout " + getSubject() + ": " + e);
+ } finally {
+ accessControlContext = null;
}
log.debug("Closed " + this);
}
private Subject getSubject() {
- return Subject.getSubject(initialContext);
+ return Subject.getSubject(accessControlContext);
}
public Set<SecretKey> getSecretKeys() {
+ checkValid();
return getSubject().getPrivateCredentials(SecretKey.class);
}
public Session newDataSession(String cn, String workspace, Repository repository) {
+ checkValid();
return login(repository, workspace);
}
public synchronized Session getDataSession(String cn, String workspace, Repository repository) {
+ checkValid();
// FIXME make it more robust
if (workspace == null)
workspace = NodeConstants.SYS_WORKSPACE;
return !isClosed();
}
- protected boolean isClosed() {
+ private void checkValid() {
+ if (!isValid())
+ throw new IllegalStateException("CMS session " + uuid + " is not valid since " + end);
+ }
+
+ final protected boolean isClosed() {
return getEnd() != null;
}
@Override
public Authorization getAuthorization() {
+ checkValid();
return authorization;
}
@Override
public void registerView(String uid, Object view) {
+ checkValid();
if (views.containsKey(uid))
throw new IllegalArgumentException("View " + uid + " is already registered.");
views.put(uid, view);
--- /dev/null
+package org.argeo.cms.internal.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.api.PublishNamespace;
+import org.argeo.osgi.util.FilterRequirement;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.Version;
+import org.osgi.framework.VersionRange;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.framework.wiring.FrameworkWiring;
+import org.osgi.resource.Requirement;
+
+public class PkgServlet extends HttpServlet {
+ private static final long serialVersionUID = 7660824185145214324L;
+
+ private BundleContext bundleContext = FrameworkUtil.getBundle(PkgServlet.class).getBundleContext();
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String pathInfo = req.getPathInfo();
+
+ String pkg, versionStr, file;
+ String[] parts = pathInfo.split("/");
+ // first is always empty
+ if (parts.length == 4) {
+ pkg = parts[1];
+ versionStr = parts[2];
+ file = parts[3];
+ } else if (parts.length == 3) {
+ pkg = parts[1];
+ versionStr = null;
+ file = parts[2];
+ } else {
+ throw new IllegalArgumentException("Unsupported path length " + pathInfo);
+ }
+
+ FrameworkWiring frameworkWiring = bundleContext.getBundle(0).adapt(FrameworkWiring.class);
+ String filter;
+ if (versionStr == null) {
+ filter = "(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + pkg + ")";
+ } else {
+ if (versionStr.startsWith("[") || versionStr.startsWith("(")) {// range
+ VersionRange versionRange = new VersionRange(versionStr);
+ filter = "(&(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + pkg + ")"
+ + versionRange.toFilterString(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE) + ")";
+
+ } else {
+ Version version = new Version(versionStr);
+ filter = "(&(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + pkg + ")("
+ + PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE + "=" + version + "))";
+ }
+ }
+ Requirement requirement = new FilterRequirement(PackageNamespace.PACKAGE_NAMESPACE, filter);
+ Collection<BundleCapability> packages = frameworkWiring.findProviders(requirement);
+ if (packages.isEmpty()) {
+ resp.sendError(404);
+ return;
+ }
+
+ // TODO verify that it works with multiple versions
+ SortedMap<Version, BundleCapability> sorted = new TreeMap<>();
+ for (BundleCapability capability : packages) {
+ sorted.put(capability.getRevision().getVersion(), capability);
+ }
+
+ Bundle bundle = sorted.get(sorted.firstKey()).getRevision().getBundle();
+ String entryPath = '/' + pkg.replace('.', '/') + '/' + file;
+ URL internalURL = bundle.getResource(entryPath);
+ if (internalURL == null) {
+ resp.sendError(404);
+ return;
+ }
+
+ // Resource found, we now check whether it can be published
+ boolean publish = false;
+ BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
+ capabilities: for (BundleCapability bundleCapability : bundleWiring
+ .getCapabilities(PublishNamespace.CMS_PUBLISH_NAMESPACE)) {
+ Object publishedPkg = bundleCapability.getAttributes().get(PublishNamespace.PKG);
+ if (publishedPkg != null) {
+ if (publishedPkg.equals("*") || publishedPkg.equals(pkg)) {
+ Object publishedFile = bundleCapability.getAttributes().get(PublishNamespace.FILE);
+ if (publishedFile == null) {
+ publish = true;
+ break capabilities;
+ } else {
+ String[] publishedFiles = publishedFile.toString().split(",");
+ for (String pattern : publishedFiles) {
+ if (pattern.startsWith("*.")) {
+ String ext = pattern.substring(1);
+ if (file.endsWith(ext)) {
+ publish = true;
+ break capabilities;
+ }
+ } else {
+ if (publishedFile.equals(file)) {
+ publish = true;
+ break capabilities;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!publish) {
+ resp.sendError(404);
+ return;
+ }
+
+ try (InputStream in = internalURL.openStream()) {
+ IOUtils.copy(in, resp.getOutputStream());
+ }
+ }
+
+}
import java.util.Properties;
+import org.apache.jackrabbit.core.config.BeanConfig;
import org.apache.jackrabbit.core.config.ConfigurationException;
import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
import org.apache.jackrabbit.core.config.WorkspaceSecurityConfig;
*/
@SuppressWarnings("restriction")
class CustomRepositoryConfigurationParser extends RepositoryConfigurationParser {
- private ClassLoader accessControlProviderClassLoader = null;
+ private ClassLoader classLoader = null;
public CustomRepositoryConfigurationParser(Properties variables) {
super(variables);
props.putAll(variables);
CustomRepositoryConfigurationParser subParser = new CustomRepositoryConfigurationParser(props,
connectionFactory);
- subParser.setAccessControlProviderClassLoader(accessControlProviderClassLoader);
+ subParser.setClassLoader(classLoader);
return subParser;
}
@Override
public WorkspaceSecurityConfig parseWorkspaceSecurityConfig(Element parent) throws ConfigurationException {
WorkspaceSecurityConfig workspaceSecurityConfig = super.parseWorkspaceSecurityConfig(parent);
- workspaceSecurityConfig.getAccessControlProviderConfig().setClassLoader(accessControlProviderClassLoader);
+ workspaceSecurityConfig.getAccessControlProviderConfig().setClassLoader(classLoader);
return workspaceSecurityConfig;
}
- public void setAccessControlProviderClassLoader(ClassLoader accessControlProviderClassLoader) {
- this.accessControlProviderClassLoader = accessControlProviderClassLoader;
+ @Override
+ protected BeanConfig parseBeanConfig(Element parent, String name) throws ConfigurationException {
+ BeanConfig beanConfig = super.parseBeanConfig(parent, name);
+ if (beanConfig.getClassName().startsWith("org.argeo")) {
+ beanConfig.setClassLoader(classLoader);
+ }
+ return beanConfig;
+ }
+
+ public void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
}
}
--- /dev/null
+package org.argeo.cms.internal.jcr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.apache.jackrabbit.core.data.DataIdentifier;
+import org.apache.jackrabbit.core.data.DataRecord;
+import org.apache.jackrabbit.core.data.DataStoreException;
+import org.apache.jackrabbit.core.data.FileDataStore;
+
+/**
+ * <b>experimental</b> Duplicate added entries in another directory (typically a
+ * remote mount).
+ */
+@SuppressWarnings("restriction")
+public class LocalFsDataStore extends FileDataStore {
+ String redundantPath;
+ FileDataStore redundantStore;
+
+ @Override
+ public void init(String homeDir) {
+ // init primary first
+ super.init(homeDir);
+
+ if (redundantPath != null) {
+ // redundant directory must be created first
+ // TODO implement some polling?
+ if (Files.exists(Paths.get(redundantPath))) {
+ redundantStore = new FileDataStore();
+ redundantStore.setPath(redundantPath);
+ redundantStore.init(homeDir);
+ }
+ }
+ }
+
+ @Override
+ public DataRecord addRecord(InputStream input) throws DataStoreException {
+ DataRecord dataRecord = super.addRecord(input);
+ syncRedundantRecord(dataRecord);
+ return dataRecord;
+ }
+
+ @Override
+ public DataRecord getRecord(DataIdentifier identifier) throws DataStoreException {
+ DataRecord dataRecord = super.getRecord(identifier);
+ syncRedundantRecord(dataRecord);
+ return dataRecord;
+ }
+
+ protected void syncRedundantRecord(DataRecord dataRecord) throws DataStoreException {
+ if (redundantStore == null)
+ return;
+ if (redundantStore.getRecordIfStored(dataRecord.getIdentifier()) == null) {
+ try (InputStream redundant = dataRecord.getStream()) {
+ redundantStore.addRecord(redundant);
+ } catch (IOException e) {
+ throw new DataStoreException("Cannot add redundant record.", e);
+ }
+ }
+ }
+
+ public void setRedundantPath(String redundantPath) {
+ this.redundantPath = redundantPath;
+ }
+
+}
// custom configuration parser
CustomRepositoryConfigurationParser parser = new CustomRepositoryConfigurationParser(jackrabbitVars);
- parser.setAccessControlProviderClassLoader(cl);
+ parser.setClassLoader(cl);
RepositoryConfig repositoryConfig = parser.parseRepositoryConfig(config);
repositoryConfig.init();
</DataSources>
<!-- File system and datastore -->
- <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+ <FileSystem
+ class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
<param name="dataSourceName" value="dataSource" />
<param name="schema" value="postgresql" />
<param name="schemaObjectPrefix" value="fs_" />
</FileSystem>
- <DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
+ <DataStore
+ class="org.argeo.cms.internal.jcr.LocalFsDataStore">
<param name="path" value="${rep.home}/../datastore" />
+ <param name="redundantPath" value="${rep.home}/../datastorer" />
</DataStore>
<!-- Workspace templates -->
<Workspaces rootPath="${rep.home}/workspaces"
defaultWorkspace="${defaultWorkspace}" />
<Workspace name="${wsp.name}">
- <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+ <FileSystem
+ class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
<param name="dataSourceName" value="dataSource" />
<param name="schema" value="postgresql" />
<param name="schemaObjectPrefix" value="${wsp.name}_fs_" />
<param name="schemaObjectPrefix" value="${wsp.name}_pm_" />
<param name="bundleCacheSize" value="${bundleCacheMB}" />
</PersistenceManager>
- <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
- <param name="path" value="${indexesBase}/${cn}/${wsp.name}/index" />
+ <SearchIndex
+ class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path"
+ value="${indexesBase}/${cn}/${wsp.name}/index" />
<param name="extractorPoolSize" value="${extractorPoolSize}" />
<param name="cacheSize" value="${searchCacheSize}" />
- <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+ <param name="maxVolatileIndexSize"
+ value="${maxVolatileIndexSize}" />
</SearchIndex>
<WorkspaceSecurity>
<AccessControlProvider
<!-- Versioning -->
<Versioning rootPath="${rep.home}/version">
- <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
+ <FileSystem
+ class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
<param name="dataSourceName" value="dataSource" />
<param name="schema" value="postgresql" />
<param name="schemaObjectPrefix" value="fs_ver_" />
</Versioning>
<!-- Indexing -->
- <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <SearchIndex
+ class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
<param name="path" value="${indexesBase}/${cn}/index" />
<param name="extractorPoolSize" value="${extractorPoolSize}" />
<param name="cacheSize" value="${searchCacheSize}" />
- <param name="maxVolatileIndexSize" value="${maxVolatileIndexSize}" />
+ <param name="maxVolatileIndexSize"
+ value="${maxVolatileIndexSize}" />
</SearchIndex>
<!-- Security -->
<Security appName="Jackrabbit">
- <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
+ <SecurityManager
+ class="org.argeo.security.jackrabbit.ArgeoSecurityManager"
workspaceName="security" />
- <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
+ <AccessManager
+ class="org.argeo.security.jackrabbit.ArgeoAccessManager" />
</Security>
<!-- Clustering -->
- <Cluster id="${clusterId}">
- <Journal class="org.apache.jackrabbit.core.journal.DatabaseJournal">
+ <Cluster id="${clusterId}" syncDelay="100">
+ <Journal
+ class="org.apache.jackrabbit.core.journal.DatabaseJournal">
<param name="dataSourceName" value="dataSource" />
<param name="schemaObjectPrefix" value="journal_" />
</Journal>
return getNodeUserAdmin().getAcceptorCredentials();
}
+ @Deprecated
public static boolean isSingleUser() {
return getNodeUserAdmin().isSingleUser();
}
@Override
public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
String uri = (String) properties.get(UserAdminConf.uri.name());
+ Object realm = properties.get(UserAdminConf.realm.name());
URI u;
try {
if (uri == null) {
String baseDn = (String) properties.get(UserAdminConf.baseDn.name());
u = KernelUtils.getOsgiInstanceUri(KernelConstants.DIR_NODE + '/' + baseDn + ".ldif");
- } else
+ } else if (realm != null) {
+ u = null;
+ } else {
u = new URI(uri);
+ }
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Badly formatted URI " + uri, e);
}
// Create
AbstractUserDirectory userDirectory;
- if (UserAdminConf.SCHEME_LDAP.equals(u.getScheme())) {
+ if (realm != null || UserAdminConf.SCHEME_LDAP.equals(u.getScheme())
+ || UserAdminConf.SCHEME_LDAPS.equals(u.getScheme())) {
userDirectory = new LdapUserAdmin(properties);
} else if (UserAdminConf.SCHEME_FILE.equals(u.getScheme())) {
userDirectory = new LdifUserAdmin(u, properties);
} else {
throw new IllegalArgumentException("Unsupported scheme " + u.getScheme());
}
- Object realm = userDirectory.getProperties().get(UserAdminConf.realm.name());
addUserDirectory(userDirectory);
// OSGi
pidToBaseDn.put(pid, baseDn);
// pidToServiceRegs.put(pid, reg);
- if (log.isDebugEnabled())
- log.debug("User directory " + userDirectory.getBaseDn() + " [" + u.getScheme() + "] enabled."
- + (realm != null ? " " + realm + " realm." : ""));
+ if (log.isDebugEnabled()) {
+ log.debug("User directory " + userDirectory.getBaseDn() + (u != null ? " [" + u.getScheme() + "]" : "")
+ + " enabled." + (realm != null ? " " + realm + " realm." : ""));
+ }
if (isSystemRolesBaseDn(baseDn)) {
// publishes only when system roles are available
import org.osgi.service.http.context.ServletContextHelper;
/**
- * Default servlet context degrading to anonymous if the the sesison is not
+ * Default servlet context degrading to anonymous if the the session is not
* pre-authenticated.
*/
public class CmsServletContext extends ServletContextHelper {
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="ext/test"/>
<classpathentry kind="con"
path="org.eclipse.pde.core.requiredPlugins" />
<classpathentry kind="con"
- path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
+ path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11" />
<classpathentry kind="output" path="bin" />
</classpath>
widget.setData(RWT.MARKUP_ENABLED, true);
}
+ public static void setMarkupValidationDisabledData(Widget widget) {
+ widget.setData("org.eclipse.rap.rwt.markupValidationDisabled", Boolean.TRUE);
+ }
+
/**
* TootlTip support is supported only for {@link AbstractTableViewer} in RAP
*/
<classpathentry kind="con"
path="org.eclipse.pde.core.requiredPlugins" />
<classpathentry kind="con"
- path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
+ path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11" />
<classpathentry kind="output" path="bin" />
</classpath>
<classpathentry kind="con"
path="org.eclipse.pde.core.requiredPlugins" />
<classpathentry kind="con"
- path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
+ path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11" />
<classpathentry kind="output" path="bin" />
</classpath>
}
/** Ordered, with preferred first. */
- public List<String> getSrvRecordsAsHosts(String name) throws NamingException {
+ public List<String> getSrvRecordsAsHosts(String name, boolean withPort) throws NamingException {
List<String> raw = getRecords(name, "SRV");
if (raw.size() == 0)
return null;
}
List<String> lst = new ArrayList<>();
for (SrvRecord order : res) {
- lst.add(order.toHost());
+ lst.add(order.toHost(withPort));
}
return Collections.unmodifiableList(lst);
}
return priority - other.priority;
if (weight != other.weight)
return other.weight - other.weight;
- String host = toHost();
- String otherHost = other.toHost();
+ String host = toHost(false);
+ String otherHost = other.toHost(false);
if (host.length() == otherHost.length())
- return toHost().compareTo(other.toHost());
+ return host.compareTo(otherHost);
else
return host.length() - otherHost.length();
}
return priority + " " + weight;
}
- public String toHost() {
+ public String toHost(boolean withPort) {
String hostStr = hostname;
if (hostname.charAt(hostname.length() - 1) == '.')
hostStr = hostname.substring(0, hostname.length() - 1);
- return hostStr + ":" + port;
+ return hostStr + (withPort ? ":" + port : "");
}
}
private final boolean readOnly;
private final boolean disabled;
- private final URI uri;
+ private final String uri;
private UserAdmin externalRoles;
// private List<String> indexedUserProperties = Arrays
// .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(),
// LdapAttrs.cn.name() });
+ private final boolean scoped;
+
private String memberAttributeId = "member";
private List<String> credentialAttributeIds = Arrays
.asList(new String[] { LdapAttrs.userPassword.name(), LdapAttrs.authPassword.name() });
private TransactionManager transactionManager;
private WcXaResource xaResource = new WcXaResource(this);
- public AbstractUserDirectory(URI uriArg, Dictionary<String, ?> props) {
+ AbstractUserDirectory(URI uriArg, Dictionary<String, ?> props, boolean scoped) {
+ this.scoped = scoped;
properties = new Hashtable<String, Object>();
for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
String key = keys.nextElement();
}
if (uriArg != null) {
- uri = uriArg;
+ uri = uriArg.toString();
// uri from properties is ignored
} else {
String uriStr = UserAdminConf.uri.getValue(properties);
if (uriStr == null)
uri = null;
else
- try {
- uri = new URI(uriStr);
- } catch (URISyntaxException e) {
- throw new UserDirectoryException("Badly formatted URI " + uriStr, e);
- }
+ uri = uriStr;
}
userObjectClass = UserAdminConf.userObjectClass.getValue(properties);
Attributes attrs = user.getAttributes();
// TODO centralize attribute name
Attribute memberOf = attrs.get(LdapAttrs.memberOf.name());
- if (memberOf != null) {
+ // if user belongs to this directory, we only check meberOf
+ if (memberOf != null && user.getDn().startsWith(getBaseDn())) {
try {
NamingEnumeration<?> values = memberOf.getAll();
while (values.hasMore()) {
Object value = values.next();
LdapName groupDn = new LdapName(value.toString());
DirectoryUser group = doGetRole(groupDn);
- allRoles.add(group);
+ if (group != null)
+ allRoles.add(group);
}
} catch (Exception e) {
throw new UserDirectoryException("Cannot get memberOf groups for " + user, e);
for (LdapName groupDn : getDirectGroups(user.getDn())) {
// TODO check for loops
DirectoryUser group = doGetRole(groupDn);
- allRoles.add(group);
- collectRoles(group, allRoles);
+ if (group != null) {
+ allRoles.add(group);
+ collectRoles(group, allRoles);
+ }
}
}
}
return credentialAttributeIds;
}
- protected URI getUri() {
+ protected String getUri() {
return uri;
}
- private static boolean readOnlyDefault(URI uri) {
- if (uri == null)
+ private static boolean readOnlyDefault(String uriStr) {
+ if (uriStr == null)
return true;
+ /// TODO make it more generic
+ URI uri;
+ try {
+ uri = new URI(uriStr.split(" ")[0]);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
if (uri.getScheme() == null)
return false;// assume relative file to be writable
if (uri.getScheme().equals(UserAdminConf.SCHEME_FILE)) {
return xaResource;
}
+ public boolean isScoped() {
+ return scoped;
+ }
+
}
public User getUser(String key, String value) {
List<User> res = new ArrayList<User>();
for (UserAdmin userAdmin : businessRoles.values()) {
- User u = userAdmin.getUser(key, value);
- if (u != null)
- res.add(u);
+ User u = userAdmin.getUser(key, value);
+ if (u != null)
+ res.add(u);
}
// Note: node roles cannot contain users, so it is not searched
return res.size() == 1 ? res.get(0) : null;
if (user == null) {// anonymous
return systemRoles.getAuthorization(null);
}
- UserAdmin userAdmin = findUserAdmin(user.getName());
- Authorization rawAuthorization = userAdmin.getAuthorization(user);
+ AbstractUserDirectory userReferentialOfThisUser = findUserAdmin(user.getName());
+ Authorization rawAuthorization = userReferentialOfThisUser.getAuthorization(user);
String usernameToUse;
String displayNameToUse;
if (user instanceof Group) {
+ // TODO check whether this is still working
String ownerDn = TokenUtils.userDn((Group) user);
if (ownerDn != null) {// tokens
UserAdmin ownerUserAdmin = findUserAdmin(ownerDn);
usernameToUse = rawAuthorization.getName();
displayNameToUse = rawAuthorization.toString();
}
- // gather system roles
- Set<String> sysRoles = new HashSet<String>();
- for (String role : rawAuthorization.getRoles()) {
- Authorization auth = systemRoles.getAuthorization((User) userAdmin.getRole(role));
- systemRoles: for (String systemRole : auth.getRoles()) {
- if (role.equals(systemRole))
- continue systemRoles;
- sysRoles.add(systemRole);
- }
+
+ // gather roles from other referentials
+ final AbstractUserDirectory userAdminToUse;// possibly scoped when authenticating
+ if (user instanceof DirectoryUser) {
+ userAdminToUse = userReferentialOfThisUser;
+ } else if (user instanceof AuthenticatingUser) {
+ userAdminToUse = userReferentialOfThisUser.scope(user);
+ } else {
+ throw new IllegalArgumentException("Unsupported user type " + user.getClass());
+ }
+
+ try {
+ Set<String> sysRoles = new HashSet<String>();
+ for (String role : rawAuthorization.getRoles()) {
+ User userOrGroup = (User) userAdminToUse.getRole(role);
+ Authorization auth = systemRoles.getAuthorization(userOrGroup);
+ systemRoles: for (String systemRole : auth.getRoles()) {
+ if (role.equals(systemRole))
+ continue systemRoles;
+ sysRoles.add(systemRole);
+ }
// sysRoles.addAll(Arrays.asList(auth.getRoles()));
+ }
+ addAbstractSystemRoles(rawAuthorization, sysRoles);
+ Authorization authorization = new AggregatingAuthorization(usernameToUse, displayNameToUse, sysRoles,
+ rawAuthorization.getRoles());
+ return authorization;
+ } finally {
+ if (userAdminToUse != null && userAdminToUse.isScoped()) {
+ userAdminToUse.destroy();
+ }
}
- addAbstractSystemRoles(rawAuthorization, sysRoles);
- Authorization authorization = new AggregatingAuthorization(usernameToUse, displayNameToUse, sysRoles,
- rawAuthorization.getRoles());
- return authorization;
}
/**
protected void postAdd(AbstractUserDirectory userDirectory) {
}
- private UserAdmin findUserAdmin(String name) {
+// private UserAdmin findUserAdmin(User user) {
+// if (user == null)
+// throw new IllegalArgumentException("User should not be null");
+// AbstractUserDirectory userAdmin = findUserAdmin(user.getName());
+// if (user instanceof DirectoryUser) {
+// return userAdmin;
+// } else {
+// return userAdmin.scope(user);
+// }
+// }
+
+ private AbstractUserDirectory findUserAdmin(String name) {
try {
- UserAdmin userAdmin = findUserAdmin(new LdapName(name));
- return userAdmin;
+ return findUserAdmin(new LdapName(name));
} catch (InvalidNameException e) {
throw new UserDirectoryException("Badly formatted name " + name, e);
}
}
- private UserAdmin findUserAdmin(LdapName name) {
+ private AbstractUserDirectory findUserAdmin(LdapName name) {
if (name.startsWith(systemRolesBaseDn))
return systemRoles;
if (tokensBaseDn != null && name.startsWith(tokensBaseDn))
package org.argeo.osgi.useradmin;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
import javax.naming.ldap.LdapName;
+import org.argeo.naming.DnsBrowser;
import org.argeo.naming.LdapAttrs;
/** Free IPA specific conventions. */
public final static String IPA_USER_DIRECTORY_CONFIG = UserAdminConf.userBase + "=" + IPA_USER_BASE + "&"
+ UserAdminConf.groupBase + "=" + IPA_GROUP_BASE + "&" + UserAdminConf.readOnly + "=true";
+ @Deprecated
static String domainToUserDirectoryConfigPath(String realm) {
return domainToBaseDn(realm) + "?" + IPA_USER_DIRECTORY_CONFIG + "&" + UserAdminConf.realm.name() + "=" + realm;
}
+ public static void addIpaConfig(String realm, Dictionary<String, Object> properties) {
+ properties.put(UserAdminConf.baseDn.name(), domainToBaseDn(realm));
+ properties.put(UserAdminConf.realm.name(), realm);
+ properties.put(UserAdminConf.userBase.name(), IPA_USER_BASE);
+ properties.put(UserAdminConf.groupBase.name(), IPA_GROUP_BASE);
+ properties.put(UserAdminConf.readOnly.name(), Boolean.TRUE.toString());
+ }
+
public static String domainToBaseDn(String domain) {
String[] dcs = domain.split("\\.");
StringBuilder sb = new StringBuilder();
private IpaUtils() {
}
+
+ public static String kerberosDomainFromDns() {
+ String kerberosDomain;
+ try (DnsBrowser dnsBrowser = new DnsBrowser()) {
+ InetAddress localhost = InetAddress.getLocalHost();
+ String hostname = localhost.getHostName();
+ String dnsZone = hostname.substring(hostname.indexOf('.') + 1);
+ kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT");
+ return kerberosDomain;
+ } catch (Exception e) {
+ throw new UserDirectoryException("Cannot determine Kerberos domain from DNS", e);
+ }
+
+ }
+
+ public static Dictionary<String, Object> convertIpaUri(URI uri) {
+ String path = uri.getPath();
+ String kerberosRealm;
+ if (path == null || path.length() <= 1) {
+ kerberosRealm = kerberosDomainFromDns();
+ } else {
+ kerberosRealm = path.substring(1);
+ }
+
+ if (kerberosRealm == null)
+ throw new UserDirectoryException("No Kerberos domain available for " + uri);
+ // TODO intergrate CA certificate in truststore
+ // String schemeToUse = SCHEME_LDAPS;
+ String schemeToUse = UserAdminConf.SCHEME_LDAP;
+ List<String> ldapHosts;
+ String ldapHostsStr = uri.getHost();
+ if (ldapHostsStr == null || ldapHostsStr.trim().equals("")) {
+ try (DnsBrowser dnsBrowser = new DnsBrowser()) {
+ ldapHosts = dnsBrowser.getSrvRecordsAsHosts("_ldap._tcp." + kerberosRealm.toLowerCase(),
+ schemeToUse.equals(UserAdminConf.SCHEME_LDAP) ? true : false);
+ if (ldapHosts == null || ldapHosts.size() == 0) {
+ throw new UserDirectoryException("Cannot configure LDAP for IPA " + uri);
+ } else {
+ ldapHostsStr = ldapHosts.get(0);
+ }
+ } catch (NamingException | IOException e) {
+ throw new UserDirectoryException("cannot convert IPA uri " + uri, e);
+ }
+ } else {
+ ldapHosts = new ArrayList<>();
+ ldapHosts.add(ldapHostsStr);
+ }
+
+ StringBuilder uriStr = new StringBuilder();
+ try {
+ for (String host : ldapHosts) {
+ URI convertedUri = new URI(schemeToUse + "://" + host + "/");
+ uriStr.append(convertedUri).append(' ');
+ }
+ } catch (URISyntaxException e) {
+ throw new UserDirectoryException("cannot convert IPA uri " + uri, e);
+ }
+
+ Hashtable<String, Object> res = new Hashtable<>();
+ res.put(UserAdminConf.uri.name(), uriStr.toString());
+ addIpaConfig(kerberosRealm, res);
+ return res;
+ }
}
--- /dev/null
+package org.argeo.osgi.useradmin;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.naming.CommunicationException;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapName;
+
+import org.argeo.naming.LdapAttrs;
+
+/** A synchronized wrapper for a single {@link InitialLdapContext}. */
+// TODO implement multiple contexts and connection pooling.
+class LdapConnection {
+ private InitialLdapContext initialLdapContext = null;
+
+ LdapConnection(String url, Dictionary<String, ?> properties) {
+ try {
+ Hashtable<String, Object> connEnv = new Hashtable<String, Object>();
+ connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ connEnv.put(Context.PROVIDER_URL, url);
+ connEnv.put("java.naming.ldap.attributes.binary", LdapAttrs.userPassword.name());
+ // use pooling in order to avoid connection timeout
+// connEnv.put("com.sun.jndi.ldap.connect.pool", "true");
+// connEnv.put("com.sun.jndi.ldap.connect.pool.timeout", 300000);
+
+ initialLdapContext = new InitialLdapContext(connEnv, null);
+ // StartTlsResponse tls = (StartTlsResponse) ctx
+ // .extendedOperation(new StartTlsRequest());
+ // tls.negotiate();
+ Object securityAuthentication = properties.get(Context.SECURITY_AUTHENTICATION);
+ if (securityAuthentication != null)
+ initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, securityAuthentication);
+ else
+ initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
+ Object principal = properties.get(Context.SECURITY_PRINCIPAL);
+ if (principal != null) {
+ initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, principal.toString());
+ Object creds = properties.get(Context.SECURITY_CREDENTIALS);
+ if (creds != null) {
+ initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, creds.toString());
+ }
+ }
+ } catch (Exception e) {
+ throw new UserDirectoryException("Cannot connect to LDAP", e);
+ }
+
+ }
+
+ public void init() {
+
+ }
+
+ public void destroy() {
+ try {
+ // tls.close();
+ initialLdapContext.close();
+ initialLdapContext = null;
+ } catch (NamingException e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected InitialLdapContext getLdapContext() {
+ return initialLdapContext;
+ }
+
+ protected void reconnect() throws NamingException {
+ initialLdapContext.reconnect(initialLdapContext.getConnectControls());
+ }
+
+ public synchronized NamingEnumeration<SearchResult> search(LdapName searchBase, String searchFilter,
+ SearchControls searchControls) throws NamingException {
+ NamingEnumeration<SearchResult> results;
+ try {
+ results = getLdapContext().search(searchBase, searchFilter, searchControls);
+ } catch (CommunicationException e) {
+ reconnect();
+ results = getLdapContext().search(searchBase, searchFilter, searchControls);
+ }
+ return results;
+ }
+
+ public synchronized Attributes getAttributes(LdapName name) throws NamingException {
+ try {
+ return getLdapContext().getAttributes(name);
+ } catch (CommunicationException e) {
+ reconnect();
+ return getLdapContext().getAttributes(name);
+ }
+ }
+
+ synchronized void prepareChanges(UserDirectoryWorkingCopy wc) throws NamingException {
+ // make sure connection will work
+ reconnect();
+
+ // delete
+ for (LdapName dn : wc.getDeletedUsers().keySet()) {
+ if (!entryExists(dn))
+ throw new UserDirectoryException("User to delete no found " + dn);
+ }
+ // add
+ for (LdapName dn : wc.getNewUsers().keySet()) {
+ if (entryExists(dn))
+ throw new UserDirectoryException("User to create found " + dn);
+ }
+ // modify
+ for (LdapName dn : wc.getModifiedUsers().keySet()) {
+ if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn))
+ throw new UserDirectoryException("User to modify not found " + dn);
+ }
+
+ }
+
+ protected boolean entryExists(LdapName dn) throws NamingException {
+ try {
+ return getAttributes(dn).size() != 0;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ synchronized void commitChanges(UserDirectoryWorkingCopy wc) throws NamingException {
+ // delete
+ for (LdapName dn : wc.getDeletedUsers().keySet()) {
+ getLdapContext().destroySubcontext(dn);
+ }
+ // add
+ for (LdapName dn : wc.getNewUsers().keySet()) {
+ DirectoryUser user = wc.getNewUsers().get(dn);
+ getLdapContext().createSubcontext(dn, user.getAttributes());
+ }
+ // modify
+ for (LdapName dn : wc.getModifiedUsers().keySet()) {
+ Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
+ getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
+ }
+ }
+}
import java.util.ArrayList;
import java.util.Dictionary;
-import java.util.Hashtable;
import java.util.List;
+import javax.naming.AuthenticationNotSupportedException;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InvalidNameException;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
-import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
-import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapName;
import javax.transaction.TransactionManager;
-import org.argeo.naming.LdapAttrs;
import org.osgi.framework.Filter;
import org.osgi.service.useradmin.Role;
import org.osgi.service.useradmin.User;
* and an open transaction for write access.
*/
public class LdapUserAdmin extends AbstractUserDirectory {
- private InitialLdapContext initialLdapContext = null;
-
-// private LdapName adminUserDn = null;
-// private LdifUser adminUser = null;
+ private LdapConnection ldapConnection;
public LdapUserAdmin(Dictionary<String, ?> properties) {
- super(null, properties);
- try {
- Hashtable<String, Object> connEnv = new Hashtable<String, Object>();
- connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
- connEnv.put(Context.PROVIDER_URL, getUri().toString());
- connEnv.put("java.naming.ldap.attributes.binary", LdapAttrs.userPassword.name());
+ this(properties, false);
+ }
- initialLdapContext = new InitialLdapContext(connEnv, null);
- // StartTlsResponse tls = (StartTlsResponse) ctx
- // .extendedOperation(new StartTlsRequest());
- // tls.negotiate();
- Object securityAuthentication = properties.get(Context.SECURITY_AUTHENTICATION);
- if (securityAuthentication != null)
- initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, securityAuthentication);
- else
- initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
- Object principal = properties.get(Context.SECURITY_PRINCIPAL);
- if (principal != null) {
- initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, principal.toString());
-// adminUserDn = new LdapName(principal.toString());
-// BasicAttributes adminUserAttrs = new BasicAttributes();
-// adminUser = new LdifUser(this, adminUserDn, adminUserAttrs);
- Object creds = properties.get(Context.SECURITY_CREDENTIALS);
- if (creds != null) {
- initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, creds.toString());
-// adminUserAttrs.put(LdapAttrs.userPassword.name(), adminUser.hash(creds.toString().toCharArray()));
- }
-// adminUserAttrs.put(LdapAttrs.memberOf.name(), "cn=admin,ou=roles,ou=node");
- }
- } catch (Exception e) {
- throw new UserDirectoryException("Cannot connect to LDAP", e);
- }
+ public LdapUserAdmin(Dictionary<String, ?> properties, boolean scoped) {
+ super(null, properties, scoped);
+ ldapConnection = new LdapConnection(getUri().toString(), properties);
}
public void destroy() {
- try {
- // tls.close();
- initialLdapContext.close();
- } catch (NamingException e) {
- e.printStackTrace();
- }
+ ldapConnection.destroy();
}
@Override
} else {
properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
}
- return new LdapUserAdmin(properties);
+ return new LdapUserAdmin(properties, true);
}
- protected InitialLdapContext getLdapContext() {
- return initialLdapContext;
- }
+// protected InitialLdapContext getLdapContext() {
+// return initialLdapContext;
+// }
@Override
protected Boolean daoHasRole(LdapName dn) {
@Override
protected DirectoryUser daoGetRole(LdapName name) throws NameNotFoundException {
try {
- Attributes attrs = getLdapContext().getAttributes(name);
+ Attributes attrs = ldapConnection.getAttributes(name);
if (attrs.size() == 0)
return null;
int roleType = roleType(name);
throw new UserDirectoryException("Unsupported LDAP type for " + name);
return res;
} catch (NameNotFoundException e) {
-// if (adminUserDn != null && adminUserDn.equals(name)) {
-// return adminUser;
-// }
throw e;
} catch (NamingException e) {
return null;
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
LdapName searchBase = getBaseDn();
- NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
+ NamingEnumeration<SearchResult> results = ldapConnection.search(searchBase, searchFilter, searchControls);
results: while (results.hasMoreElements()) {
SearchResult searchResult = results.next();
res.add(role);
}
return res;
-// } catch (NameNotFoundException e) {
-// return res;
+ } catch (AuthenticationNotSupportedException e) {
+ // ignore (typically an unsupported anonymous bind)
+ // TODO better logging
+ return res;
} catch (Exception e) {
+ e.printStackTrace();
throw new UserDirectoryException("Cannot get roles for filter " + f, e);
}
}
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
LdapName searchBase = getBaseDn();
- NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
+ NamingEnumeration<SearchResult> results = ldapConnection.search(searchBase, searchFilter, searchControls);
while (results.hasMoreElements()) {
SearchResult searchResult = (SearchResult) results.nextElement();
@Override
protected void prepare(UserDirectoryWorkingCopy wc) {
try {
- getLdapContext().reconnect(getLdapContext().getConnectControls());
- // delete
- for (LdapName dn : wc.getDeletedUsers().keySet()) {
- if (!entryExists(dn))
- throw new UserDirectoryException("User to delete no found " + dn);
- }
- // add
- for (LdapName dn : wc.getNewUsers().keySet()) {
- if (entryExists(dn))
- throw new UserDirectoryException("User to create found " + dn);
- }
- // modify
- for (LdapName dn : wc.getModifiedUsers().keySet()) {
- if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn))
- throw new UserDirectoryException("User to modify not found " + dn);
- }
+ ldapConnection.prepareChanges(wc);
} catch (NamingException e) {
throw new UserDirectoryException("Cannot prepare LDAP", e);
}
}
- private boolean entryExists(LdapName dn) throws NamingException {
- try {
- return getLdapContext().getAttributes(dn).size() != 0;
- } catch (NameNotFoundException e) {
- return false;
- }
- }
-
@Override
protected void commit(UserDirectoryWorkingCopy wc) {
try {
- // delete
- for (LdapName dn : wc.getDeletedUsers().keySet()) {
- getLdapContext().destroySubcontext(dn);
- }
- // add
- for (LdapName dn : wc.getNewUsers().keySet()) {
- DirectoryUser user = wc.getNewUsers().get(dn);
- getLdapContext().createSubcontext(dn, user.getAttributes());
- }
- // modify
- for (LdapName dn : wc.getModifiedUsers().keySet()) {
- Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
- getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
- }
+ ldapConnection.commitChanges(wc);
} catch (NamingException e) {
throw new UserDirectoryException("Cannot commit LDAP", e);
}
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
private SortedMap<LdapName, DirectoryGroup> groups = new TreeMap<LdapName, DirectoryGroup>();
public LdifUserAdmin(String uri, String baseDn) {
- this(fromUri(uri, baseDn));
+ this(fromUri(uri, baseDn), false);
}
public LdifUserAdmin(Dictionary<String, ?> properties) {
- super(null, properties);
+ this(properties, false);
+ }
+
+ protected LdifUserAdmin(Dictionary<String, ?> properties, boolean scoped) {
+ super(null, properties, scoped);
}
public LdifUserAdmin(URI uri, Dictionary<String, ?> properties) {
- super(uri, properties);
+ super(uri, properties, false);
}
@Override
}
Dictionary<String, Object> properties = cloneProperties();
properties.put(UserAdminConf.readOnly.name(), "true");
- LdifUserAdmin scopedUserAdmin = new LdifUserAdmin(properties);
+ LdifUserAdmin scopedUserAdmin = new LdifUserAdmin(properties, true);
scopedUserAdmin.groups = Collections.unmodifiableSortedMap(groups);
scopedUserAdmin.users = Collections.unmodifiableSortedMap(users);
return scopedUserAdmin;
}
public void init() {
+
try {
- if (getUri().getScheme().equals("file")) {
- File file = new File(getUri());
+ URI u = new URI(getUri());
+ if (u.getScheme().equals("file")) {
+ File file = new File(u);
if (!file.exists())
return;
}
- load(getUri().toURL().openStream());
+ load(u.toURL().openStream());
} catch (Exception e) {
throw new UserDirectoryException("Cannot open URL " + getUri(), e);
}
throw new UserDirectoryException("Cannot save LDIF user admin: no URI is set");
if (isReadOnly())
throw new UserDirectoryException("Cannot save LDIF user admin: " + getUri() + " is read-only");
- try (FileOutputStream out = new FileOutputStream(new File(getUri()))) {
+ try (FileOutputStream out = new FileOutputStream(new File(new URI(getUri())))) {
save(out);
- } catch (IOException e) {
+ } catch (IOException | URISyntaxException e) {
throw new UserDirectoryException("Cannot save user admin to " + getUri(), e);
}
}
private final LdifUser osUser;
public OsUserDirectory(URI uriArg, Dictionary<String, ?> props) {
- super(uriArg, props);
+ super(uriArg, props, false);
try {
osUserDn = new LdapName(LdapAttrs.uid.name() + "=" + osUsername + "," + getUserBase() + "," + getBaseDn());
Attributes attributes = new BasicAttributes();
@Override
protected List<DirectoryUser> doGetRoles(Filter f) {
List<DirectoryUser> res = new ArrayList<>();
- if (f==null || f.match(osUser.getProperties()))
+ if (f == null || f.match(osUser.getProperties()))
res.add(osUser);
return res;
}
package org.argeo.osgi.useradmin;
-import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import javax.naming.Context;
-import javax.naming.NamingException;
import javax.naming.ldap.LdapName;
-import org.argeo.naming.DnsBrowser;
import org.argeo.naming.NamingUtils;
/** Properties used to configure user admins. */
public final static String FACTORY_PID = "org.argeo.osgi.useradmin.config";
public final static String SCHEME_LDAP = "ldap";
+ public final static String SCHEME_LDAPS = "ldaps";
public final static String SCHEME_FILE = "file";
public final static String SCHEME_OS = "os";
public final static String SCHEME_IPA = "ipa";
URI u = new URI(uriStr);
String scheme = u.getScheme();
if (scheme != null && scheme.equals(SCHEME_IPA)) {
- u = convertIpaConfig(u);
- scheme = u.getScheme();
+ return IpaUtils.convertIpaUri(u);
+// scheme = u.getScheme();
}
String path = u.getPath();
// base DN
String principal = null;
String credentials = null;
if (scheme != null)
- if (scheme.equals(SCHEME_LDAP) || scheme.equals("ldaps")) {
+ if (scheme.equals(SCHEME_LDAP) || scheme.equals(SCHEME_LDAPS)) {
// TODO additional checks
if (u.getUserInfo() != null) {
String[] userInfo = u.getUserInfo().split(":");
}
}
- private static URI convertIpaConfig(URI uri) {
- String path = uri.getPath();
- String kerberosRealm;
- if (path == null || path.length() <= 1) {
- kerberosRealm = kerberosDomainFromDns();
- } else {
- kerberosRealm = path.substring(1);
- }
-
- if (kerberosRealm == null)
- throw new UserDirectoryException("No Kerberos domain available for " + uri);
- try (DnsBrowser dnsBrowser = new DnsBrowser()) {
- String ldapHostsStr = uri.getHost();
- if (ldapHostsStr == null || ldapHostsStr.trim().equals("")) {
- List<String> ldapHosts = dnsBrowser.getSrvRecordsAsHosts("_ldap._tcp." + kerberosRealm.toLowerCase());
- if (ldapHosts == null || ldapHosts.size() == 0) {
- throw new UserDirectoryException("Cannot configure LDAP for IPA " + uri);
- } else {
- ldapHostsStr = ldapHosts.get(0);
- }
- }
- URI convertedUri = new URI(
- SCHEME_LDAP + "://" + ldapHostsStr + "/" + IpaUtils.domainToUserDirectoryConfigPath(kerberosRealm));
- return convertedUri;
- } catch (NamingException | IOException | URISyntaxException e) {
- throw new UserDirectoryException("cannot convert IPA uri " + uri, e);
- }
- }
-
- private static String kerberosDomainFromDns() {
- String kerberosDomain;
- try (DnsBrowser dnsBrowser = new DnsBrowser()) {
- InetAddress localhost = InetAddress.getLocalHost();
- String hostname = localhost.getHostName();
- String dnsZone = hostname.substring(hostname.indexOf('.') + 1);
- kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT");
- return kerberosDomain;
- } catch (Exception e) {
- throw new UserDirectoryException("Cannot determine Kerberos domain from DNS", e);
- }
-
- }
-
private static String getBaseDnFromHostname() {
String hostname;
try {
try {
userDirectory.prepare(wc);
} catch (Exception e) {
+ e.printStackTrace();
throw new XAException(XAException.XAER_RMERR);
}
return XA_OK;
userDirectory.prepare(wc);
userDirectory.commit(wc);
} catch (Exception e) {
+ e.printStackTrace();
throw new XAException(XAException.XAER_RMERR);
} finally {
cleanUp(xid);
checkXid(xid);
userDirectory.rollback(wc(xid));
} catch (Exception e) {
+ e.printStackTrace();
throw new XAException(XAException.XAER_RMERR);
} finally {
cleanUp(xid);
--- /dev/null
+package org.argeo.osgi.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class FilterRequirement implements Requirement {
+ private String namespace;
+ private String filter;
+
+
+
+ public FilterRequirement(String namespace, String filter) {
+ this.namespace = namespace;
+ this.filter = filter;
+ }
+
+ @Override
+ public Resource getResource() {
+ return null;
+ }
+
+ @Override
+ public String getNamespace() {
+ return namespace;
+ }
+
+ @Override
+ public Map<String, String> getDirectives() {
+ Map<String, String> directives = new HashMap<>();
+ directives.put(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter);
+ return directives;
+ }
+
+ @Override
+ public Map<String, Object> getAttributes() {
+ return new HashMap<>();
+ }
+
+}
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="output" path="bin"/>
</classpath>
// QUERY
/** Creates a JCR-SQL2 query using {@link MessageFormat}. */
public static Query createQuery(QueryManager qm, String sql, Object... args) {
+ // fix single quotes
+ sql = sql.replaceAll("'", "''");
String query = MessageFormat.format(sql, args);
try {
return qm.createQuery(query, Query.JCR_SQL2);
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
return new String(hexChars);
}
+ /** Export a subtree as a compact XML without namespaces. */
+ public static void toSimpleXml(Node node, StringBuilder sb) throws RepositoryException {
+ sb.append('<');
+ String nodeName = node.getName();
+ int colIndex = nodeName.indexOf(':');
+ if (colIndex > 0) {
+ nodeName = nodeName.substring(colIndex + 1);
+ }
+ sb.append(nodeName);
+ PropertyIterator pit = node.getProperties();
+ properties: while (pit.hasNext()) {
+ Property p = pit.nextProperty();
+ // skip multiple properties
+ if (p.isMultiple())
+ continue properties;
+ String propertyName = p.getName();
+ int pcolIndex = propertyName.indexOf(':');
+ // skip properties with namespaces
+ if (pcolIndex > 0)
+ continue properties;
+ // skip binaries
+ if (p.getType() == PropertyType.BINARY) {
+ continue properties;
+ // TODO retrieve identifier?
+ }
+ sb.append(' ');
+ sb.append(propertyName);
+ sb.append('=');
+ sb.append('\"').append(p.getString()).append('\"');
+ }
+
+ if (node.hasNodes()) {
+ sb.append('>');
+ NodeIterator children = node.getNodes();
+ while (children.hasNext()) {
+ toSimpleXml(children.nextNode(), sb);
+ }
+ sb.append("</");
+ sb.append(nodeName);
+ sb.append('>');
+ } else {
+ sb.append("/>");
+ }
+ }
+
}
* XML
*/
/**
- * Set as a subnode which will be exported as an XML element.
+ * Get the XML text of this child node.
*/
public static String getXmlValue(Node node, String name) {
try {
if (!node.hasNode(name))
return null;
Node child = node.getNode(name);
- if (!child.hasNode(Jcr.JCR_XMLTEXT))
+ return getXmlValue(child);
+ } catch (RepositoryException e) {
+ throw new IllegalStateException("Cannot get " + name + " as XML text", e);
+ }
+ }
+
+ /**
+ * Get the XML text of this node.
+ */
+ public static String getXmlValue(Node node) {
+ try {
+ if (!node.hasNode(Jcr.JCR_XMLTEXT))
return null;
- Node xmlText = child.getNode(Jcr.JCR_XMLTEXT);
+ Node xmlText = node.getNode(Jcr.JCR_XMLTEXT);
if (!xmlText.hasProperty(Jcr.JCR_XMLCHARACTERS))
throw new IllegalArgumentException(
"Node " + xmlText + " has no " + Jcr.JCR_XMLCHARACTERS + " property");
return xmlText.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
} catch (RepositoryException e) {
- throw new IllegalStateException("Cannot get " + name + " as XML text", e);
+ throw new IllegalStateException("Cannot get " + node + " as XML text", e);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
}
/** Add a user or group to a group. */
- protected void addToGroup(String roledDn, String groupDn) {
- if (roledDn.contentEquals(groupDn)) {
+ protected void addToGroup(String groupToAddDn, String groupDn) {
+ if (groupToAddDn.contentEquals(groupDn)) {
if (log.isTraceEnabled())
log.trace("Ignore adding group " + groupDn + " to itself");
return;
}
if (getUserAdmin() == null) {
- log.warn("No user admin service available, cannot add group " + roledDn + " to " + groupDn);
+ log.warn("No user admin service available, cannot add group " + groupToAddDn + " to " + groupDn);
return;
}
- Group managerGroup = (Group) getUserAdmin().getRole(roledDn);
+ Group groupToAdd = (Group) getUserAdmin().getRole(groupToAddDn);
+ if (groupToAdd == null)
+ throw new IllegalArgumentException("Group " + groupToAddDn + " not found");
Group group = (Group) getUserAdmin().getRole(groupDn);
if (group == null)
throw new IllegalArgumentException("Group " + groupDn + " not found");
try {
getUserTransaction().begin();
- if (group.addMember(managerGroup))
- log.info("Added " + roledDn + " to " + group);
+ if (group.addMember(groupToAdd))
+ log.info("Added " + groupToAddDn + " to " + group);
getUserTransaction().commit();
} catch (Exception e) {
try {
} catch (Exception e1) {
// silent
}
- throw new IllegalStateException("Cannot add " + managerGroup + " to " + group);
+ throw new IllegalStateException("Cannot add " + groupToAddDn + " to " + groupDn);
}
}
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="ext/test"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="output" path="bin"/>
</classpath>