You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by en...@apache.org on 2022/01/04 19:51:34 UTC
[sling-org-apache-sling-jcr-jackrabbit-usermanager] branch master updated: SLING-11034 expose nested authorizable properties as child resources (#9)
This is an automated email from the ASF dual-hosted git repository.
enorman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-jackrabbit-usermanager.git
The following commit(s) were added to refs/heads/master by this push:
new 62d5643 SLING-11034 expose nested authorizable properties as child resources (#9)
62d5643 is described below
commit 62d5643c901a213c9c0c80ca8555f3b2514efd71
Author: Eric Norman <er...@gmail.com>
AuthorDate: Tue Jan 4 11:43:08 2022 -0800
SLING-11034 expose nested authorizable properties as child resources (#9)
---
.../impl/resource/AuthorizableResource.java | 37 +-
.../resource/AuthorizableResourceProvider.java | 253 ++++++-
.../impl/resource/AuthorizableValueMap.java | 438 +-----------
...ValueMap.java => BaseAuthorizableValueMap.java} | 186 ++---
.../impl/resource/NestedAuthorizableResource.java | 69 ++
.../impl/resource/NestedAuthorizableValueMap.java | 85 +++
.../AuthorizablePrivilegesInfoTest.java | 41 ++
.../impl/resource/LazyInputStreamTest.java | 134 ++++
.../resource/AuthorizableResourceProviderIT.java | 276 +++++---
.../it/resource/AuthorizableValueMapIT.java | 191 ++++++
.../it/resource/BaseAuthorizableResourcesIT.java | 153 +++++
.../it/resource/BaseAuthorizableValueMapIT.java | 759 +++++++++++++++++++++
.../it/resource/NestedAuthorizableResourcesIT.java | 369 ++++++++++
.../it/resource/NestedAuthorizableValueMapIT.java | 92 +++
.../resource/NoNestedAuthorizableResourcesIT.java | 141 ++++
15 files changed, 2539 insertions(+), 685 deletions(-)
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableResource.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableResource.java
index 6f18b72..01c66e4 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableResource.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableResource.java
@@ -41,17 +41,12 @@ import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
@Adapter(condition="If the resource is an AuthorizableResource and represents a JCR Group", value = Group.class)
})
public class AuthorizableResource extends AbstractResource {
- private Authorizable authorizable = null;
-
- private ResourceResolver resourceResolver = null;
-
+ protected final ResourceResolver resourceResolver;
+ protected final Authorizable authorizable;
private final String path;
-
private final String resourceType;
-
private final ResourceMetadata metadata;
-
- private final SystemUserManagerPaths systemUserManagerPaths;
+ protected final SystemUserManagerPaths systemUserManagerPaths;
public AuthorizableResource(Authorizable authorizable,
ResourceResolver resourceResolver, String path,
@@ -62,16 +57,25 @@ public class AuthorizableResource extends AbstractResource {
this.authorizable = authorizable;
this.path = path;
this.systemUserManagerPaths = systemUserManagerPaths;
- if (authorizable.isGroup()) {
- this.resourceType = "sling/group";
- } else {
- this.resourceType = "sling/user";
- }
+ this.resourceType = toResourceType(authorizable);
this.metadata = new ResourceMetadata();
metadata.setResolutionPath(path);
}
+ /**
+ * determine the resource type for the authorizable.
+ * @param authorizable the authorizable to consider
+ * @return the resource type
+ */
+ protected String toResourceType(Authorizable authorizable) {
+ if (authorizable.isGroup()) {
+ return "sling/group";
+ } else {
+ return "sling/user";
+ }
+ }
+
/*
* (non-Javadoc)
* @see org.apache.sling.api.resource.Resource#getPath()
@@ -117,15 +121,14 @@ public class AuthorizableResource extends AbstractResource {
* @see org.apache.sling.api.adapter.Adaptable#adaptTo(java.lang.Class)
*/
@Override
- @SuppressWarnings("unchecked")
public <T> T adaptTo(Class<T> type) {
if (type == Map.class || type == ValueMap.class) {
- return (T) new AuthorizableValueMap(authorizable, systemUserManagerPaths); // unchecked
- // cast
+ ValueMap valueMap = new AuthorizableValueMap(authorizable, systemUserManagerPaths);
+ return type.cast(valueMap);
} else if (type == Authorizable.class
|| (type == User.class && !authorizable.isGroup())
|| (type == Group.class && authorizable.isGroup())) {
- return (T) authorizable;
+ return type.cast(authorizable);
}
return super.adaptTo(type);
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableResourceProvider.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableResourceProvider.java
index a5930bd..b67f58e 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableResourceProvider.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableResourceProvider.java
@@ -18,9 +18,9 @@ package org.apache.sling.jackrabbit.usermanager.impl.resource;
import java.security.Principal;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.NoSuchElementException;
import javax.jcr.RepositoryException;
@@ -40,6 +40,8 @@ import org.apache.sling.jcr.base.util.AccessControlUtil;
import org.apache.sling.spi.resource.provider.ResolveContext;
import org.apache.sling.spi.resource.provider.ResourceContext;
import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.metatype.annotations.AttributeDefinition;
@@ -66,6 +68,11 @@ public class AuthorizableResourceProvider extends ResourceProvider<Object> imple
@AttributeDefinition(name = "Provider Root",
description = "Specifies the root path for the UserManager resources.")
String provider_root() default DEFAULT_SYSTEM_USER_MANAGER_PATH; //NOSONAR
+
+ @AttributeDefinition(name = "Provide Resources For Nested Properties",
+ description = "Specifies whether container resources are provided for any nested authorizable properties. "
+ + "The resourceType for these ancestor resources would be 'sling/[user|group]/properties'")
+ boolean resources_for_nested_properties() default false; //NOSONAR
}
/**
@@ -115,13 +122,16 @@ public class AuthorizableResourceProvider extends ResourceProvider<Object> imple
public static final String SYSTEM_USER_MANAGER_GROUP_PREFIX = SYSTEM_USER_MANAGER_GROUP_PATH //NOSONAR
+ "/";
+ private boolean resourcesForNestedProperties = true;
+
@Activate
- protected void activate(final Map<String, Object> props) {
- systemUserManagerPath = OsgiUtil.toString(props.get(ResourceProvider.PROPERTY_ROOT), DEFAULT_SYSTEM_USER_MANAGER_PATH);
+ protected void activate(final Config config) {
+ systemUserManagerPath = OsgiUtil.toString(config.provider_root(), DEFAULT_SYSTEM_USER_MANAGER_PATH);
systemUserManagerUserPath = String.format("%s/user", systemUserManagerPath);
systemUserManagerUserPrefix = String.format("%s/", systemUserManagerUserPath);
systemUserManagerGroupPath = String.format("%s/group", systemUserManagerPath);
systemUserManagerGroupPrefix = String.format("%s/", systemUserManagerGroupPath);
+ resourcesForNestedProperties = config.resources_for_nested_properties();
}
/* (non-Javadoc)
@@ -180,40 +190,89 @@ public class AuthorizableResourceProvider extends ResourceProvider<Object> imple
return new SyntheticResource(ctx.getResourceResolver(), path, "sling/groups");
}
+ AuthorizableWorker<Resource> worker = (authorizable, relPath) -> {
+ Resource result = null;
+ // found the Authorizable, so return the resource
+ // that wraps it.
+ if (relPath == null) {
+ result = new AuthorizableResource(authorizable,
+ ctx.getResourceResolver(), path,
+ AuthorizableResourceProvider.this);
+ } else if (resourcesForNestedProperties) {
+ // check if the relPath resolves valid property names
+ Iterator<String> propertyNames = getPropertyNames(relPath, authorizable);
+ if (propertyNames.hasNext()) {
+ // provide a resource that wraps for the specific nested properties
+ result = new NestedAuthorizableResource(authorizable,
+ ctx.getResourceResolver(), path,
+ AuthorizableResourceProvider.this,
+ relPath);
+ }
+ }
+ return result;
+ };
+ return maybeDoAuthorizableWork(ctx, path, worker);
+ }
+
+ /**
+ * If the path resolves to a user or group (with optional relPath suffix)
+ * then invoke the worker to do some work.
+ */
+ protected <T> T maybeDoAuthorizableWork(@NotNull ResolveContext<Object> ctx, @NotNull String path, @NotNull AuthorizableWorker<T> worker) {
+ T result = null;
// the principalId should be the first segment after the prefix
- String pid = null;
+ String suffix = null;
if (path.startsWith(systemUserManagerUserPrefix)) {
- pid = path.substring(systemUserManagerUserPrefix.length());
+ suffix = path.substring(systemUserManagerUserPrefix.length());
} else if (path.startsWith(systemUserManagerGroupPrefix)) {
- pid = path.substring(systemUserManagerGroupPrefix.length());
+ suffix = path.substring(systemUserManagerGroupPrefix.length());
}
- if (pid != null) {
- if (pid.indexOf('/') != -1) {
- return null; // something bogus on the end of the path so bail
- // out now.
+ if (suffix != null) {
+ String pid;
+ String relPath;
+ int firstSlash = suffix.indexOf('/');
+ if (firstSlash == -1) {
+ pid = suffix;
+ relPath = null;
+ } else {
+ pid = suffix.substring(0, firstSlash);
+ relPath = suffix.substring(firstSlash + 1);
}
- try {
- Session session = ctx.getResourceResolver().adaptTo(Session.class);
- if (session != null) {
+ Session session = ctx.getResourceResolver().adaptTo(Session.class);
+ if (session != null) {
+ try {
UserManager userManager = AccessControlUtil.getUserManager(session);
if (userManager != null) {
Authorizable authorizable = userManager.getAuthorizable(pid);
if (authorizable != null) {
- // found the Authorizable, so return the resource
- // that wraps it.
- return new AuthorizableResource(authorizable,
- ctx.getResourceResolver(), path,
- AuthorizableResourceProvider.this);
+ result = worker.doWork(authorizable, relPath);
}
}
+ } catch (RepositoryException re) {
+ throw new SlingException(
+ "Error looking up Authorizable for principal: " + pid, re);
}
- } catch (RepositoryException re) {
- throw new SlingException(
- "Error looking up Authorizable for principal: " + pid, re);
}
}
- return null;
+ return result;
+ }
+
+ protected static Iterator<String> getPropertyNames(String relPath, Authorizable authorizable) {
+ Iterator<String> propertyNames;
+ try {
+ // TODO: there isn't any way to check if relPath is valid
+ // as this call throws an exception instead of returning null
+ // or an empty iterator.
+ propertyNames = authorizable.getPropertyNames(relPath);
+ } catch (RepositoryException re) {
+ Logger logger = LoggerFactory.getLogger(AuthorizableResourceProvider.class);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to get property names", re);
+ }
+ propertyNames = Collections.emptyIterator();
+ }
+ return propertyNames;
}
@Override
@@ -249,6 +308,25 @@ public class AuthorizableResourceProvider extends ResourceProvider<Object> imple
if (principals != null) {
return new ChildrenIterator(parent, principals);
}
+ } else if (resourcesForNestedProperties) {
+ // handle nested property containers
+
+ AuthorizableWorker<Iterator<Resource>> worker = (authorizable, relPath) -> {
+ Iterator<Resource> result = null;
+ Resource r = ctx.getResourceResolver().resolve(authorizable.getPath());
+ if (relPath != null) {
+ r = r.getChild(relPath);
+ }
+ if (r != null) {
+ // only include the children that are nested property containers
+ List<Resource> propContainers = filterPropertyContainers(relPath, authorizable, r);
+ if (!propContainers.isEmpty()) {
+ result = new NestedChildrenIterator(parent, authorizable.getID(), r.getChildren().iterator());
+ }
+ }
+ return result;
+ };
+ return maybeDoAuthorizableWork(ctx, path, worker);
}
} catch (RepositoryException re) {
throw new SlingException("Error listing children of resource: "
@@ -258,45 +336,76 @@ public class AuthorizableResourceProvider extends ResourceProvider<Object> imple
return null;
}
- private final class ChildrenIterator implements Iterator<Resource> {
- private PrincipalIterator principals;
+ /**
+ * Filter the resource children to return only the resources that are
+ * nested property containers
+ *
+ * @param relPath the relative path to start from
+ * @param authorizable the user or group
+ * @param r the resource to filter the children of
+ * @return list of resources that are property containers
+ */
+ protected List<Resource> filterPropertyContainers(String relPath, Authorizable authorizable, Resource r) {
+ List<Resource> propContainers = new ArrayList<>();
+ for (Resource cr : r.getChildren()) {
+ String childRelPath;
+ if (relPath == null) {
+ childRelPath = cr.getName();
+ } else {
+ childRelPath = String.format("%s/%s", relPath, cr.getName());
+ }
+ if (getPropertyNames(childRelPath, authorizable).hasNext()) {
+ propContainers.add(cr);
+ } else {
+ // child is not a property container?
+ if (log.isDebugEnabled()) {
+ log.debug("skipping child that is not appear to be a nested property container: {}", cr.getName());
+ }
+ }
+ }
+ return propContainers;
+ }
+ private abstract class BaseChildrenIterator implements Iterator<Resource> {
private Resource parent;
+ private Iterator<?> children;
- public ChildrenIterator(Resource parent, PrincipalIterator principals) {
+ private BaseChildrenIterator(Resource parent, Iterator<?> children) {
this.parent = parent;
- this.principals = principals;
+ this.children = children;
}
+ @Override
public boolean hasNext() {
- return principals.hasNext();
+ return children.hasNext();
}
+ @Override
public Resource next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
- Principal nextPrincipal = principals.nextPrincipal();
+ Resource next = null;
+ Object child = children.next();
+ String principalName = toPrincipalName(child);
try {
ResourceResolver resourceResolver = parent.getResourceResolver();
Session session = resourceResolver.adaptTo(Session.class);
if (session != null) {
UserManager userManager = AccessControlUtil.getUserManager(session);
if (userManager != null) {
- Authorizable authorizable = userManager.getAuthorizable(nextPrincipal.getName());
+ Authorizable authorizable = userManager.getAuthorizable(principalName);
if (authorizable != null) {
String path;
if (authorizable.isGroup()) {
path = systemUserManagerGroupPrefix
- + nextPrincipal.getName();
+ + principalName;
} else {
path = systemUserManagerUserPrefix
- + nextPrincipal.getName();
+ + principalName;
}
- return new AuthorizableResource(authorizable,
- resourceResolver, path,
- AuthorizableResourceProvider.this);
+ next = createNext(child, resourceResolver, authorizable, path);
}
}
}
@@ -304,13 +413,83 @@ public class AuthorizableResourceProvider extends ResourceProvider<Object> imple
log.error("Exception while looking up authorizable resource.",
re);
}
- return null;
+ return next;
+ }
+
+ protected abstract String toPrincipalName(Object child);
+
+ protected abstract Resource createNext(Object child, ResourceResolver resourceResolver,
+ Authorizable authorizable, String path) throws RepositoryException;
+
+ }
+
+ private final class NestedChildrenIterator extends BaseChildrenIterator {
+
+ private String principalName;
+
+ private NestedChildrenIterator(Resource parent, String principalName, Iterator<Resource> children) {
+ super(parent, children);
+ this.principalName = principalName;
+ }
+
+ @Override
+ protected String toPrincipalName(Object child) {
+ return principalName;
}
@Override
- public void remove() {
- throw new UnsupportedOperationException();
+ protected Resource createNext(Object child, ResourceResolver resourceResolver, Authorizable authorizable,
+ String path) throws RepositoryException {
+ Resource next = null;
+ if (child instanceof Resource) {
+ Resource childResource = (Resource)child;
+ //calculate the path relative to the home folder root
+ String relPath = childResource.getPath().substring(authorizable.getPath().length() + 1);
+
+ // check if the relPath resolves any valid property names
+ Iterator<String> propertyNames = getPropertyNames(relPath, authorizable);
+ if (propertyNames.hasNext()) {
+ next = new NestedAuthorizableResource(authorizable,
+ resourceResolver, String.format("%s/%s", path, relPath),
+ AuthorizableResourceProvider.this,
+ relPath);
+ }
+ }
+ return next;
}
+
+ }
+
+ private final class ChildrenIterator extends BaseChildrenIterator {
+
+ public ChildrenIterator(Resource parent, PrincipalIterator principals) {
+ super(parent, principals);
+ }
+
+ @Override
+ protected String toPrincipalName(Object child) {
+ String principalName = null;
+ if (child instanceof Principal) {
+ principalName = ((Principal)child).getName();
+ }
+ return principalName;
+ }
+
+ @Override
+ protected Resource createNext(Object child, ResourceResolver resourceResolver, Authorizable authorizable,
+ String path) throws RepositoryException {
+ return new AuthorizableResource(authorizable,
+ resourceResolver, path,
+ AuthorizableResourceProvider.this);
+ }
+
+ }
+
+ /**
+ * Interface for lambda expressions to do work on a resolved authorizable + optional relative path
+ */
+ protected static interface AuthorizableWorker<T> {
+ public T doWork(@NotNull Authorizable authorizable, @Nullable String relPath) throws RepositoryException;
}
}
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableValueMap.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableValueMap.java
index 541b600..308219b 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableValueMap.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableValueMap.java
@@ -16,218 +16,62 @@
*/
package org.apache.sling.jackrabbit.usermanager.impl.resource;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Array;
import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Date;
import java.util.Iterator;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.jcr.Property;
-import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
-import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
- * ValueMap implementation for Authorizable Resources
+ * ValueMap implementation for the root property container of Authorizable Resources
*/
-public class AuthorizableValueMap implements ValueMap {
-
+public class AuthorizableValueMap extends BaseAuthorizableValueMap {
private static final String DECLARED_MEMBERS_KEY = "declaredMembers";
-
private static final String MEMBERS_KEY = "members";
-
private static final String DECLARED_MEMBER_OF_KEY = "declaredMemberOf";
-
private static final String MEMBER_OF_KEY = "memberOf";
-
private static final String PATH_KEY = "path";
- private static final Logger LOG = LoggerFactory.getLogger(AuthorizableValueMap.class);
-
- private boolean fullyRead;
-
- private final Map<String, Object> cache;
-
- private Authorizable authorizable;
-
- private final SystemUserManagerPaths systemUserManagerPaths;
-
public AuthorizableValueMap(Authorizable authorizable, SystemUserManagerPaths systemUserManagerPaths) {
- this.authorizable = authorizable;
- this.cache = new LinkedHashMap<>();
- this.fullyRead = false;
- this.systemUserManagerPaths = systemUserManagerPaths;
+ super(authorizable, systemUserManagerPaths);
}
@Override
- @SuppressWarnings("unchecked")
- public <T> T get(String name, Class<T> type) {
- if (type == null) {
- return (T) get(name);
- }
-
- return convertToType(name, type);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public <T> T get(String name, T defaultValue) {
- if (defaultValue == null) {
- return (T) get(name);
- }
-
- // special handling in case the default value implements one
- // of the interface types supported by the convertToType method
- Class<T> type = (Class<T>) normalizeClass(defaultValue.getClass());
-
- T value = get(name, type);
- if (value == null) {
- value = defaultValue;
- }
-
- return value;
- }
-
- public boolean containsKey(Object key) {
- return get(key) != null;
- }
-
- public boolean containsValue(Object value) {
- readFully();
- return cache.containsValue(value);
- }
-
- public Set<java.util.Map.Entry<String, Object>> entrySet() {
- readFully();
- return cache.entrySet();
- }
-
- public Object get(Object key) {
- Object value = cache.get(key);
- if (value == null) {
- value = read((String) key);
- }
-
- return value;
- }
-
- public Set<String> keySet() {
- readFully();
- return cache.keySet();
- }
-
- public int size() {
- readFully();
- return cache.size();
- }
-
- public boolean isEmpty() {
- return size() == 0;
- }
-
- public Collection<Object> values() {
- readFully();
- return cache.values();
- }
-
protected Object read(String key) {
+ Object value = null;
// if the item has been completely read, we need not check
// again, as we certainly will not find the key
- if (fullyRead) {
- return null;
- }
-
- try {
- if (key.equals(MEMBERS_KEY) && authorizable.isGroup()) {
- return getMembers((Group) authorizable, true);
- }
- if (key.equals(DECLARED_MEMBERS_KEY) && authorizable.isGroup()) {
- return getMembers((Group) authorizable, false);
- }
- if (key.equals(MEMBER_OF_KEY)) {
- return getMemberships(true);
- }
- if (key.equals(DECLARED_MEMBER_OF_KEY)) {
- return getMemberships(false);
- }
- if (key.equals(PATH_KEY)) {
- return getPath();
- }
- if (authorizable.hasProperty(key)) {
- final Value[] property = authorizable.getProperty(key);
- final Object value = valuesToJavaObject(property);
- cache.put(key, value);
- return value;
+ if (!fullyRead) {
+ try {
+ if (key.equals(MEMBERS_KEY) && authorizable.isGroup()) {
+ value = getMembers((Group) authorizable, true);
+ } else if (key.equals(DECLARED_MEMBERS_KEY) && authorizable.isGroup()) {
+ value = getMembers((Group) authorizable, false);
+ } else if (key.equals(MEMBER_OF_KEY)) {
+ value = getMemberships(true);
+ } else if (key.equals(DECLARED_MEMBER_OF_KEY)) {
+ value = getMemberships(false);
+ } else if (key.equals(PATH_KEY)) {
+ value = getPath();
+ } else if (authorizable.hasProperty(key)) {
+ value = readPropertyAndCache(key, key);
+ } else {
+ // property not found or some error accessing it
+ }
+ } catch (RepositoryException re) {
+ log.error("Could not access authorizable property", re);
}
- } catch (RepositoryException re) {
- LOG.error("Could not access authorizable property", re);
}
- // property not found or some error accessing it
- return null;
- }
-
- /**
- * Converts a JCR Value to a corresponding Java Object
- *
- * @param value the JCR Value to convert
- * @return the Java Object
- * @throws RepositoryException if the value cannot be converted
- */
- public static Object toJavaObject(Value value) throws RepositoryException {
- switch (value.getType()) {
- case PropertyType.DECIMAL:
- return value.getDecimal();
- case PropertyType.BINARY:
- return new LazyInputStream(value);
- case PropertyType.BOOLEAN:
- return value.getBoolean();
- case PropertyType.DATE:
- return value.getDate();
- case PropertyType.DOUBLE:
- return value.getDouble();
- case PropertyType.LONG:
- return value.getLong();
- case PropertyType.NAME: // fall through
- case PropertyType.PATH: // fall through
- case PropertyType.REFERENCE: // fall through
- case PropertyType.STRING: // fall through
- case PropertyType.UNDEFINED: // not actually expected
- default: // not actually expected
- return value.getString();
- }
- }
- protected Object valuesToJavaObject(Value[] values)
- throws RepositoryException {
- if (values == null) {
- return null;
- } else if (values.length == 1) {
- return toJavaObject(values[0]);
- } else {
- Object[] valuesObjs = new Object[values.length];
- for (int i = 0; i < values.length; i++) {
- valuesObjs[i] = toJavaObject(values[i]);
- }
- return valuesObjs;
- }
+ return value;
}
+ @Override
protected void readFully() {
if (!fullyRead) {
try {
@@ -242,156 +86,21 @@ public class AuthorizableValueMap implements ValueMap {
if (path != null) {
cache.put(PATH_KEY, path);
}
+
// only direct property
Iterator<String> pi = authorizable.getPropertyNames();
while (pi.hasNext()) {
String key = pi.next();
if (!cache.containsKey(key)) {
- Value[] property = authorizable.getProperty(key);
- Object value = valuesToJavaObject(property);
- cache.put(key, value);
+ readPropertyAndCache(key, key);
}
}
fullyRead = true;
} catch (RepositoryException re) {
- LOG.error("Could not access certain properties of user {}", authorizable, re);
- }
- }
- }
-
- /**
- * Reads the authorizable map completely and returns the string
- * representation of the cached properties.
- */
- @Override
- public String toString() {
- readFully();
- return cache.toString();
- }
-
- // ---------- Unsupported Modification methods
-
- public Object remove(Object arg0) {
- throw new UnsupportedOperationException();
- }
-
- public void clear() {
- throw new UnsupportedOperationException();
- }
-
- public Object put(String arg0, Object arg1) {
- throw new UnsupportedOperationException();
- }
-
- public void putAll(Map<? extends String, ? extends Object> arg0) {
- throw new UnsupportedOperationException();
- }
-
- // ---------- Implementation helper
-
- @SuppressWarnings("unchecked")
- private <T> T convertToType(String name, Class<T> type) {
- T result = null;
-
- try {
- if (authorizable.hasProperty(name)) {
- Value[] values = authorizable.getProperty(name);
-
- if (values == null) {
- return null;
- }
-
- boolean multiValue = values.length > 1;
- boolean array = type.isArray();
-
- if (multiValue) {
- if (array) {
- result = (T) convertToArray(values,
- type.getComponentType());
- } else if (values.length > 0) {
- result = convertToType(values[0], type);
- }
- } else {
- Value value = values[0];
- if (array) {
- result = (T) convertToArray(new Value[] { value },
- type.getComponentType());
- } else {
- result = convertToType(value, type);
- }
- }
- }
-
- } catch (ValueFormatException vfe) {
- LOG.info(String.format("convertToType: Cannot convert value of %s to %s", name, type), vfe);
- } catch (RepositoryException re) {
- LOG.info(String.format("convertToType: Cannot get value of %s", name), re);
- }
-
- // fall back to nothing
- return result;
- }
-
- private <T> T[] convertToArray(Value[] jcrValues, Class<T> type)
- throws RepositoryException {
- List<T> values = new ArrayList<>();
- for (int i = 0; i < jcrValues.length; i++) {
- T value = convertToType(jcrValues[i], type);
- if (value != null) {
- values.add(value);
+ log.error("Could not access certain properties of user {}", authorizable, re);
}
}
-
- @SuppressWarnings("unchecked")
- T[] result = (T[]) Array.newInstance(type, values.size());
-
- return values.toArray(result);
- }
-
- @SuppressWarnings("unchecked")
- private <T> T convertToType(Value jcrValue, Class<T> type)
- throws RepositoryException {
-
- if (String.class == type) {
- return (T) jcrValue.getString();
- } else if (Byte.class == type) {
- return (T) Byte.valueOf((byte) jcrValue.getLong());
- } else if (Short.class == type) {
- return (T) Short.valueOf((short) jcrValue.getLong());
- } else if (Integer.class == type) {
- return (T) Integer.valueOf((int) jcrValue.getLong());
- } else if (Long.class == type) {
- return (T) Long.valueOf(jcrValue.getLong());
- } else if (Float.class == type) {
- return (T) Float.valueOf((float)jcrValue.getDouble());
- } else if (Double.class == type) {
- return (T) Double.valueOf(jcrValue.getDouble());
- } else if (Boolean.class == type) {
- return (T) Boolean.valueOf(jcrValue.getBoolean());
- } else if (Date.class == type) {
- return (T) jcrValue.getDate().getTime();
- } else if (Calendar.class == type) {
- return (T) jcrValue.getDate();
- } else if (Value.class == type) {
- return (T) jcrValue;
- }
-
- // fallback in case of unsupported type
- return null;
- }
-
- private Class<?> normalizeClass(Class<?> type) {
- if (Calendar.class.isAssignableFrom(type)) {
- type = Calendar.class;
- } else if (Date.class.isAssignableFrom(type)) {
- type = Date.class;
- } else if (Value.class.isAssignableFrom(type)) {
- type = Value.class;
- } else if (Property.class.isAssignableFrom(type)) {
- type = Property.class;
- }
- return type;
}
private String[] getMembers(Group group, boolean includeAll) throws RepositoryException {
@@ -417,98 +126,13 @@ public class AuthorizableValueMap implements ValueMap {
}
return results.toArray(new String[results.size()]);
}
-
+
private String getPath() throws RepositoryException {
try {
return authorizable.getPath();
} catch (UnsupportedRepositoryOperationException e) {
- LOG.debug("Could not retrieve path of authorizable {}", authorizable, e);
+ log.debug("Could not retrieve path of authorizable {}", authorizable, e);
return null;
}
}
-
- public static class LazyInputStream extends InputStream {
-
- /** The JCR Value from which the input stream is requested on demand */
- private final Value value;
-
- /** The inputstream created on demand, null if not used */
- private InputStream delegatee;
-
- public LazyInputStream(Value value) {
- this.value = value;
- }
-
- /**
- * Closes the input stream if acquired otherwise does nothing.
- */
- @Override
- public void close() throws IOException {
- if (delegatee != null) {
- delegatee.close();
- }
- }
-
- @Override
- public int available() throws IOException {
- return getStream().available();
- }
-
- @Override
- public int read() throws IOException {
- return getStream().read();
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- return getStream().read(b);
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- return getStream().read(b, off, len);
- }
-
- @Override
- public long skip(long n) throws IOException {
- return getStream().skip(n);
- }
-
- @Override
- public boolean markSupported() {
- try {
- return getStream().markSupported();
- } catch (IOException ioe) {
- // ignore
- }
- return false;
- }
-
- @Override
- public synchronized void mark(int readlimit) {
- try {
- getStream().mark(readlimit);
- } catch (IOException ioe) {
- // ignore
- }
- }
-
- @Override
- public synchronized void reset() throws IOException {
- getStream().reset();
- }
-
- /** Actually retrieves the input stream from the underlying JCR Value */
- private InputStream getStream() throws IOException {
- if (delegatee == null) {
- try {
- delegatee = value.getBinary().getStream();
- } catch (RepositoryException re) {
- throw (IOException) new IOException(re.getMessage()).initCause(re);
- }
- }
- return delegatee;
- }
-
- }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableValueMap.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/BaseAuthorizableValueMap.java
similarity index 67%
copy from src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableValueMap.java
copy to src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/BaseAuthorizableValueMap.java
index 541b600..e538e5c 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/AuthorizableValueMap.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/BaseAuthorizableValueMap.java
@@ -19,56 +19,45 @@ package org.apache.sling.jackrabbit.usermanager.impl.resource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
+import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.jcr.Property;
+import javax.jcr.Binary;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
-import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import org.apache.jackrabbit.api.security.user.Authorizable;
-import org.apache.jackrabbit.api.security.user.Group;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
+import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * ValueMap implementation for Authorizable Resources
+ * base implementation for ValueMap implementations for Authorizable Resources
*/
-public class AuthorizableValueMap implements ValueMap {
+public abstract class BaseAuthorizableValueMap implements ValueMap {
- private static final String DECLARED_MEMBERS_KEY = "declaredMembers";
-
- private static final String MEMBERS_KEY = "members";
-
- private static final String DECLARED_MEMBER_OF_KEY = "declaredMemberOf";
-
- private static final String MEMBER_OF_KEY = "memberOf";
-
- private static final String PATH_KEY = "path";
-
- private static final Logger LOG = LoggerFactory.getLogger(AuthorizableValueMap.class);
-
- private boolean fullyRead;
-
- private final Map<String, Object> cache;
-
- private Authorizable authorizable;
+ /**
+ * default log
+ */
+ protected final Logger log = LoggerFactory.getLogger(getClass());
- private final SystemUserManagerPaths systemUserManagerPaths;
+ protected boolean fullyRead;
+ protected final Map<String, Object> cache;
+ protected Authorizable authorizable;
+ protected final SystemUserManagerPaths systemUserManagerPaths;
- public AuthorizableValueMap(Authorizable authorizable, SystemUserManagerPaths systemUserManagerPaths) {
+ protected BaseAuthorizableValueMap(Authorizable authorizable, SystemUserManagerPaths systemUserManagerPaths) {
this.authorizable = authorizable;
this.cache = new LinkedHashMap<>();
this.fullyRead = false;
@@ -146,41 +135,13 @@ public class AuthorizableValueMap implements ValueMap {
return cache.values();
}
- protected Object read(String key) {
- // if the item has been completely read, we need not check
- // again, as we certainly will not find the key
- if (fullyRead) {
- return null;
- }
-
- try {
- if (key.equals(MEMBERS_KEY) && authorizable.isGroup()) {
- return getMembers((Group) authorizable, true);
- }
- if (key.equals(DECLARED_MEMBERS_KEY) && authorizable.isGroup()) {
- return getMembers((Group) authorizable, false);
- }
- if (key.equals(MEMBER_OF_KEY)) {
- return getMemberships(true);
- }
- if (key.equals(DECLARED_MEMBER_OF_KEY)) {
- return getMemberships(false);
- }
- if (key.equals(PATH_KEY)) {
- return getPath();
- }
- if (authorizable.hasProperty(key)) {
- final Value[] property = authorizable.getProperty(key);
- final Object value = valuesToJavaObject(property);
- cache.put(key, value);
- return value;
- }
- } catch (RepositoryException re) {
- LOG.error("Could not access authorizable property", re);
- }
+ protected abstract Object read(String key);
- // property not found or some error accessing it
- return null;
+ protected Object readPropertyAndCache(String key, String relPath) throws RepositoryException {
+ Value[] property = authorizable.getProperty(relPath);
+ Object value = valuesToJavaObject(property);
+ cache.put(key, value);
+ return value;
}
/**
@@ -228,37 +189,7 @@ public class AuthorizableValueMap implements ValueMap {
}
}
- protected void readFully() {
- if (!fullyRead) {
- try {
- if (authorizable.isGroup()) {
- cache.put(MEMBERS_KEY, getMembers((Group) authorizable, true));
- cache.put(DECLARED_MEMBERS_KEY, getMembers((Group) authorizable, false));
- }
- cache.put(MEMBER_OF_KEY, getMemberships(true));
- cache.put(DECLARED_MEMBER_OF_KEY, getMemberships(false));
-
- String path = getPath();
- if (path != null) {
- cache.put(PATH_KEY, path);
- }
- // only direct property
- Iterator<String> pi = authorizable.getPropertyNames();
- while (pi.hasNext()) {
- String key = pi.next();
- if (!cache.containsKey(key)) {
- Value[] property = authorizable.getProperty(key);
- Object value = valuesToJavaObject(property);
- cache.put(key, value);
- }
- }
-
- fullyRead = true;
- } catch (RepositoryException re) {
- LOG.error("Could not access certain properties of user {}", authorizable, re);
- }
- }
- }
+ protected abstract void readFully();
/**
* Reads the authorizable map completely and returns the string
@@ -291,7 +222,7 @@ public class AuthorizableValueMap implements ValueMap {
// ---------- Implementation helper
@SuppressWarnings("unchecked")
- private <T> T convertToType(String name, Class<T> type) {
+ protected <T> T convertToType(String name, Class<T> type) {
T result = null;
try {
@@ -321,12 +252,15 @@ public class AuthorizableValueMap implements ValueMap {
result = convertToType(value, type);
}
}
+ } else {
+ // some synthetic property not stored with the authorizable?
+ // fallback to the default impl from the ValueMap interface
+ result = ValueMap.super.get(name, type);
}
-
} catch (ValueFormatException vfe) {
- LOG.info(String.format("convertToType: Cannot convert value of %s to %s", name, type), vfe);
+ log.info(String.format("convertToType: Cannot convert value of %s to %s", name, type), vfe);
} catch (RepositoryException re) {
- LOG.info(String.format("convertToType: Cannot get value of %s", name), re);
+ log.info(String.format("convertToType: Cannot get value of %s", name), re);
}
// fall back to nothing
@@ -335,18 +269,25 @@ public class AuthorizableValueMap implements ValueMap {
private <T> T[] convertToArray(Value[] jcrValues, Class<T> type)
throws RepositoryException {
- List<T> values = new ArrayList<>();
+ // lazy create this list in case there are no valid type conversions
+ List<T> values = null;
for (int i = 0; i < jcrValues.length; i++) {
T value = convertToType(jcrValues[i], type);
if (value != null) {
+ if (values == null) {
+ values = new ArrayList<>();
+ }
values.add(value);
}
}
- @SuppressWarnings("unchecked")
- T[] result = (T[]) Array.newInstance(type, values.size());
-
- return values.toArray(result);
+ T[] array = null;
+ if (values != null) {
+ @SuppressWarnings("unchecked")
+ T[] result = (T[]) Array.newInstance(type, values.size());
+ array = values.toArray(result);
+ }
+ return array;
}
@SuppressWarnings("unchecked")
@@ -357,6 +298,8 @@ public class AuthorizableValueMap implements ValueMap {
return (T) jcrValue.getString();
} else if (Byte.class == type) {
return (T) Byte.valueOf((byte) jcrValue.getLong());
+ } else if (BigDecimal.class == type) {
+ return (T) jcrValue.getDecimal();
} else if (Short.class == type) {
return (T) Short.valueOf((short) jcrValue.getLong());
} else if (Integer.class == type) {
@@ -373,6 +316,10 @@ public class AuthorizableValueMap implements ValueMap {
return (T) jcrValue.getDate().getTime();
} else if (Calendar.class == type) {
return (T) jcrValue.getDate();
+ } else if (Binary.class == type) {
+ return (T) jcrValue.getBinary();
+ } else if (InputStream.class == type) {
+ return (T) jcrValue.getBinary().getStream();
} else if (Value.class == type) {
return (T) jcrValue;
}
@@ -388,45 +335,14 @@ public class AuthorizableValueMap implements ValueMap {
type = Date.class;
} else if (Value.class.isAssignableFrom(type)) {
type = Value.class;
- } else if (Property.class.isAssignableFrom(type)) {
- type = Property.class;
+ } else if (InputStream.class.isAssignableFrom(type)) {
+ type = InputStream.class;
+ } else if (Binary.class.isAssignableFrom(type)) {
+ type = Binary.class;
}
return type;
}
- private String[] getMembers(Group group, boolean includeAll) throws RepositoryException {
- List<String> results = new ArrayList<>();
- for (Iterator<Authorizable> it = includeAll ? group.getMembers() : group.getDeclaredMembers();
- it.hasNext();) {
- Authorizable auth = it.next();
- if (auth.isGroup()) {
- results.add(systemUserManagerPaths.getGroupPrefix() + auth.getID());
- } else {
- results.add(systemUserManagerPaths.getUserPrefix() + auth.getID());
- }
- }
- return results.toArray(new String[results.size()]);
- }
-
- private String[] getMemberships(boolean includeAll) throws RepositoryException {
- List<String> results = new ArrayList<>();
- for (Iterator<Group> it = includeAll ? authorizable.memberOf() : authorizable.declaredMemberOf();
- it.hasNext();) {
- Group group = it.next();
- results.add(systemUserManagerPaths.getGroupPrefix() + group.getID());
- }
- return results.toArray(new String[results.size()]);
- }
-
- private String getPath() throws RepositoryException {
- try {
- return authorizable.getPath();
- } catch (UnsupportedRepositoryOperationException e) {
- LOG.debug("Could not retrieve path of authorizable {}", authorizable, e);
- return null;
- }
- }
-
public static class LazyInputStream extends InputStream {
/** The JCR Value from which the input stream is requested on demand */
@@ -435,7 +351,7 @@ public class AuthorizableValueMap implements ValueMap {
/** The inputstream created on demand, null if not used */
private InputStream delegatee;
- public LazyInputStream(Value value) {
+ public LazyInputStream(@NotNull Value value) {
this.value = value;
}
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/NestedAuthorizableResource.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/NestedAuthorizableResource.java
new file mode 100644
index 0000000..677f7e2
--- /dev/null
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/NestedAuthorizableResource.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.jackrabbit.usermanager.impl.resource;
+
+import java.util.Map;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.sling.adapter.annotations.Adaptable;
+import org.apache.sling.adapter.annotations.Adapter;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
+
+/**
+ * Resource implementation for nested property containers of Authorizable
+ */
+@Adaptable(adaptableClass = Resource.class, adapters = {
+ @Adapter({Map.class, ValueMap.class, Authorizable.class}),
+ @Adapter(condition="If the resource is an AuthorizableResource and represents a JCR User", value = User.class),
+ @Adapter(condition="If the resource is an AuthorizableResource and represents a JCR Group", value = Group.class)
+})
+public class NestedAuthorizableResource extends AuthorizableResource {
+ private final String relPropPath;
+
+ public NestedAuthorizableResource(Authorizable authorizable,
+ ResourceResolver resourceResolver, String path,
+ SystemUserManagerPaths systemUserManagerPaths,
+ String relPropPath) {
+ super(authorizable, resourceResolver, path, systemUserManagerPaths);
+ this.relPropPath = relPropPath;
+ }
+
+ @Override
+ protected String toResourceType(Authorizable authorizable) {
+ return String.format("%s/properties", super.toResourceType(authorizable));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.apache.sling.api.adapter.Adaptable#adaptTo(java.lang.Class)
+ */
+ @Override
+ public <T> T adaptTo(Class<T> type) {
+ if (type == Map.class || type == ValueMap.class) {
+ ValueMap valueMap = new NestedAuthorizableValueMap(authorizable, systemUserManagerPaths, relPropPath);
+ return type.cast(valueMap);
+ }
+
+ return super.adaptTo(type);
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/NestedAuthorizableValueMap.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/NestedAuthorizableValueMap.java
new file mode 100644
index 0000000..92e3d1d
--- /dev/null
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/resource/NestedAuthorizableValueMap.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.jackrabbit.usermanager.impl.resource;
+
+import java.util.Iterator;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * ValueMap implementation for nested properties of Authorizable Resources
+ */
+public class NestedAuthorizableValueMap extends BaseAuthorizableValueMap {
+ private final String relPropPath;
+
+ public NestedAuthorizableValueMap(Authorizable authorizable, SystemUserManagerPaths systemUserManagerPaths,
+ @NotNull String relPropPath) {
+ super(authorizable, systemUserManagerPaths);
+ this.relPropPath = relPropPath;
+ }
+
+ @Override
+ protected Object read(String key) {
+ Object value = null;
+ // if the item has been completely read, we need not check
+ // again, as we certainly will not find the key
+ if (!fullyRead) {
+ try {
+ // prepend the relPath to the key
+ String relPropKey = String.format("%s/%s", relPropPath, key);
+ if (authorizable.hasProperty(relPropKey)) {
+ value = readPropertyAndCache(key, relPropKey);
+ } else {
+ // property not found or some error accessing it
+ }
+ } catch (RepositoryException re) {
+ log.error("Could not access authorizable property", re);
+ }
+ }
+
+ return value;
+ }
+
+ @Override
+ protected void readFully() {
+ if (!fullyRead) {
+ try {
+ Iterator<String> pi = AuthorizableResourceProvider.getPropertyNames(relPropPath, authorizable);
+ while (pi.hasNext()) {
+ String key = pi.next();
+ if (!cache.containsKey(key)) {
+ readPropertyAndCache(key, String.format("%s/%s", relPropPath, key));
+ }
+ }
+
+ fullyRead = true;
+ } catch (RepositoryException re) {
+ log.error("Could not access certain properties of user {}", authorizable, re);
+ }
+ }
+ }
+
+ @Override
+ protected <T> T convertToType(String name, Class<T> type) {
+ return super.convertToType(String.format("%s/%s", relPropPath, name), type);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfoTest.java b/src/test/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfoTest.java
new file mode 100644
index 0000000..b53504a
--- /dev/null
+++ b/src/test/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfoTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.jackrabbit.usermanager;
+
+import static org.junit.Assert.*;
+
+import org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo.PropertyUpdateTypes;
+import org.junit.Test;
+
+/**
+ * Test coverage for AuthorizablePrivilegesInfo / PropertyUpdateTypes
+ */
+public class AuthorizablePrivilegesInfoTest {
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testConvertDeprecated() {
+ assertEquals(PropertyUpdateTypes.ADD_PROPERTY, PropertyUpdateTypes.convertDeprecated(PropertyUpdateTypes.addProperty));
+ assertEquals(PropertyUpdateTypes.ADD_NESTED_PROPERTY, PropertyUpdateTypes.convertDeprecated(PropertyUpdateTypes.addNestedProperty));
+ assertEquals(PropertyUpdateTypes.ALTER_PROPERTY, PropertyUpdateTypes.convertDeprecated(PropertyUpdateTypes.alterProperty));
+ assertEquals(PropertyUpdateTypes.REMOVE_PROPERTY, PropertyUpdateTypes.convertDeprecated(PropertyUpdateTypes.removeProperty));
+
+ //and one that doesn't require conversion
+ assertEquals(PropertyUpdateTypes.REMOVE_PROPERTY, PropertyUpdateTypes.convertDeprecated(PropertyUpdateTypes.REMOVE_PROPERTY));
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jackrabbit/usermanager/impl/resource/LazyInputStreamTest.java b/src/test/java/org/apache/sling/jackrabbit/usermanager/impl/resource/LazyInputStreamTest.java
new file mode 100644
index 0000000..dcdf203
--- /dev/null
+++ b/src/test/java/org/apache/sling/jackrabbit/usermanager/impl/resource/LazyInputStreamTest.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.jackrabbit.usermanager.impl.resource;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.jcr.ValueFactory;
+
+import org.apache.jackrabbit.value.ValueFactoryImpl;
+import org.apache.sling.jackrabbit.usermanager.impl.resource.BaseAuthorizableValueMap.LazyInputStream;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Provides code coverage for BaseAuthorizableValueMap.LazyInputStream
+ */
+public class LazyInputStreamTest {
+
+ private LazyInputStream lazyIS = null;
+
+ @Before
+ public void setup() {
+ ValueFactory vf = ValueFactoryImpl.getInstance();
+ lazyIS = new LazyInputStream(vf.createValue("Hello World"));
+ }
+
+ public void teardown() {
+ if (lazyIS != null) {
+ try {
+ lazyIS.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ lazyIS = null;
+ }
+ }
+
+ @Test
+ public void testClose() throws IOException {
+ // first without any reads that would construct the delegatee
+ lazyIS.close();
+ //then some call that initiates te delegatee
+ assertTrue(lazyIS.available() > 0);
+ // and close again
+ lazyIS.close();
+ }
+
+ @Test
+ public void testAvailable() throws IOException {
+ assertEquals(11, lazyIS.available());
+ }
+
+ @Test
+ public void testRead() throws IOException {
+ assertEquals('H', lazyIS.read());
+ }
+
+ @Test
+ public void testReadArray() throws IOException {
+ byte[] b = new byte[5];
+ assertEquals(5, lazyIS.read(b));
+ assertArrayEquals("Hello".getBytes(), b);
+ }
+
+ @Test
+ public void testReadArrayWithOffsetAndLength() throws IOException {
+ byte[] b = "AAAAA".getBytes();
+ int off = 2;
+ int len = 3;
+ assertEquals(3, lazyIS.read(b, off, len));
+ assertArrayEquals("AAHel".getBytes(), b);
+ }
+
+ @Test
+ public void testSkip() throws IOException {
+ long n = 2;
+ lazyIS.skip(n);
+ assertEquals('l', lazyIS.read());
+ }
+
+ @Test
+ public void testMarkSupported() {
+ assertTrue(lazyIS.markSupported());
+ }
+
+ @Test
+ public void testMark() throws IOException {
+ int readlimit = 3;
+ assertEquals('H', lazyIS.read());
+ lazyIS.mark(readlimit);
+ assertEquals('e', lazyIS.read());
+ assertEquals('l', lazyIS.read());
+ lazyIS.reset();
+ assertEquals('e', lazyIS.read());
+ assertEquals('l', lazyIS.read());
+
+ //also try exceeding the readlimit
+ lazyIS.mark(readlimit);
+ lazyIS.read(new byte[readlimit + 1]);
+ lazyIS.reset();
+ byte [] whatsleft = new byte[8];
+ assertEquals(8, lazyIS.read(whatsleft));
+ assertArrayEquals("lo World".getBytes(), whatsleft);
+ }
+
+ @Test
+ public void testReset() throws IOException {
+ int readlimit = 3;
+ lazyIS.mark(readlimit);
+ assertEquals('H', lazyIS.read());
+ assertEquals('e', lazyIS.read());
+ lazyIS.reset();
+ assertEquals('H', lazyIS.read());
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/AuthorizableResourceProviderIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/AuthorizableResourceProviderIT.java
index 38d14ce..28607c6 100644
--- a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/AuthorizableResourceProviderIT.java
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/AuthorizableResourceProviderIT.java
@@ -20,94 +20,49 @@ package org.apache.sling.jcr.jackrabbit.usermanager.it.resource;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.Iterator;
+import java.util.Map;
-import javax.inject.Inject;
import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
+import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.jackrabbit.usermanager.CreateGroup;
-import org.apache.sling.jackrabbit.usermanager.CreateUser;
-import org.apache.sling.jackrabbit.usermanager.DeleteGroup;
-import org.apache.sling.jackrabbit.usermanager.DeleteUser;
+import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.jackrabbit.usermanager.impl.resource.AuthorizableResourceProvider;
import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
-import org.apache.sling.jcr.api.SlingRepository;
-import org.apache.sling.jcr.jackrabbit.usermanager.it.UserManagerTestSupport;
import org.apache.sling.jcr.resource.api.JcrResourceConstants;
-import org.junit.After;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerClass;
-import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Basic test of AuthorizableResourceProvider component
*/
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerClass.class)
-public class AuthorizableResourceProviderIT extends UserManagerTestSupport {
+public class AuthorizableResourceProviderIT extends BaseAuthorizableResourcesIT {
private static final String PEOPLE_ROOT = "/people";
- private static AtomicLong counter = new AtomicLong(0);
- private final Logger logger = LoggerFactory.getLogger(getClass());
-
- @Inject
- protected BundleContext bundleContext;
-
- @Inject
- protected SlingRepository repository;
-
- @Inject
- protected ResourceResolverFactory resourceResolverFactory;
-
- @Inject
- protected ConfigurationAdmin configAdmin;
-
- @Inject
- private CreateUser createUser;
-
- @Inject
- private CreateGroup createGroup;
-
- @Inject
- private DeleteUser deleteUser;
-
- @Inject
- private DeleteGroup deleteGroup;
-
- @Rule
- public TestName testName = new TestName();
-
- protected Session adminSession;
- protected User user1;
- protected Group group1;
@Before
- public void setup() throws RepositoryException {
- adminSession = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
- assertNotNull("Expected adminSession to not be null", adminSession);
+ public void setup() throws RepositoryException, LoginException {
+ super.setup();
user1 = createUser.createUser(adminSession, createUniqueName("user"), "testPwd", "testPwd",
Collections.emptyMap(), new ArrayList<>());
@@ -122,40 +77,6 @@ public class AuthorizableResourceProviderIT extends UserManagerTestSupport {
}
}
- @After
- public void teardown() {
- try {
- adminSession.refresh(false);
- if (user1 != null) {
- deleteUser.deleteUser(adminSession, user1.getID(), new ArrayList<>());
- }
-
- if (adminSession.hasPendingChanges()) {
- adminSession.save();
- }
- } catch (RepositoryException e) {
- logger.warn(String.format("Failed to delete user: %s", e.getMessage()), e);
- }
- try {
- adminSession.refresh(false);
- if (group1 != null) {
- deleteGroup.deleteGroup(adminSession, group1.getID(), new ArrayList<>());
- }
-
- if (adminSession.hasPendingChanges()) {
- adminSession.save();
- }
- } catch (RepositoryException e) {
- logger.warn(String.format("Failed to delete group: %s", e.getMessage()), e);
- }
-
- adminSession.logout();
- }
-
- protected String createUniqueName(String prefix) {
- return String.format("%s_%s%d", prefix, testName.getMethodName(), counter.incrementAndGet());
- }
-
/**
* Test changing the usermanager provider.root value
*/
@@ -177,6 +98,10 @@ public class AuthorizableResourceProviderIT extends UserManagerTestSupport {
serviceReference = bundleContext.getServiceReference(SystemUserManagerPaths.class);
assertEquals(PEOPLE_ROOT, serviceReference.getProperty("provider.root"));
+ SystemUserManagerPaths service = bundleContext.getService(serviceReference);
+ assertNotNull(service);
+ assertEquals(PEOPLE_ROOT, service.getRootPath());
+
// now the userManager resource should be mounted under /people
checkResourceTypes(PEOPLE_ROOT, "/system/userManager");
} finally {
@@ -240,4 +165,177 @@ public class AuthorizableResourceProviderIT extends UserManagerTestSupport {
}
}
+ /**
+ * Test iteration of the usermanager root resource children
+ */
+ @Test
+ public void listRootChildren() throws LoginException, RepositoryException, IOException {
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource = resourceResolver.resolve("/system/userManager");
+ assertTrue("Expected resource type of sling/userManager for: " + resource.getPath(),
+ resource.isResourceType("sling/userManager"));
+
+ boolean foundUsers = false;
+ boolean foundGroups = false;
+ @NotNull
+ Iterable<Resource> children = resource.getChildren();
+ for (Iterator<Resource> iterator = children.iterator(); iterator.hasNext();) {
+ Resource child = (Resource) iterator.next();
+ if (child.isResourceType("sling/users")) {
+ foundUsers = true;
+ } else if (child.isResourceType("sling/groups")) {
+ foundGroups = true;
+ }
+ }
+ assertTrue(foundUsers);
+ assertTrue(foundGroups);
+ }
+ }
+
+ /**
+ * Test iteration of the usermanager users resource children
+ */
+ @Test
+ public void listUsersChildren() throws LoginException, RepositoryException, IOException {
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource = resourceResolver.resolve("/system/userManager/user");
+ assertTrue("Expected resource type of sling/users for: " + resource.getPath(),
+ resource.isResourceType("sling/users"));
+
+ boolean foundUser = false;
+ @NotNull
+ Iterable<Resource> children = resource.getChildren();
+ for (Iterator<Resource> iterator = children.iterator(); iterator.hasNext();) {
+ Resource child = (Resource) iterator.next();
+ if (child.isResourceType("sling/user") && user1.getID().equals(child.getName())) {
+ foundUser = true;
+ }
+ }
+ assertTrue(foundUser);
+ }
+ }
+
+ /**
+ * Test iteration of the usermanager groups resource children
+ */
+ @Test
+ public void listGroupsChildren() throws LoginException, RepositoryException, IOException {
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource = resourceResolver.resolve("/system/userManager/group");
+ assertTrue("Expected resource type of sling/groups for: " + resource.getPath(),
+ resource.isResourceType("sling/groups"));
+
+ boolean foundGroup = false;
+ @NotNull
+ Iterable<Resource> children = resource.getChildren();
+ for (Iterator<Resource> iterator = children.iterator(); iterator.hasNext();) {
+ Resource child = (Resource) iterator.next();
+ if (child.isResourceType("sling/group") && group1.getID().equals(child.getName())) {
+ foundGroup = true;
+ }
+ }
+ assertTrue(foundGroup);
+ }
+ }
+
+ @Test
+ public void adaptResourceToMap() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ Map<?, ?> groupMap = groupResource.adaptTo(Map.class);
+ assertNotNull(groupMap);
+ assertEquals("value1", groupMap.get("key1"));
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ Map<?, ?> userMap = userResource.adaptTo(Map.class);
+ assertNotNull(userMap);
+ assertEquals("value1", userMap.get("key1"));
+ }
+ }
+
+ @Test
+ public void adaptResourceToValueMap() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ ValueMap groupMap = groupResource.adaptTo(ValueMap.class);
+ assertNotNull(groupMap);
+ assertEquals("AuthorizableValueMap", groupMap.getClass().getSimpleName());
+ assertEquals("value1", groupMap.get("key1"));
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ ValueMap userMap = userResource.adaptTo(ValueMap.class);
+ assertNotNull(userMap);
+ assertEquals("AuthorizableValueMap", userMap.getClass().getSimpleName());
+ assertEquals("value1", userMap.get("key1"));
+ }
+ }
+
+ @Test
+ public void adaptResourceToAuthorizable() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ Authorizable groupAuthorizable = groupResource.adaptTo(Authorizable.class);
+ assertNotNull(groupAuthorizable);
+ assertEquals(group1.getID(), groupAuthorizable.getID());
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ Authorizable userAuthorizable = userResource.adaptTo(Authorizable.class);
+ assertNotNull(userAuthorizable);
+ assertEquals(user1.getID(), userAuthorizable.getID());
+ }
+ }
+
+ @Test
+ public void adaptResourceToUserOrGroup() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ Group group = groupResource.adaptTo(Group.class);
+ assertNotNull(group);
+ assertEquals(group1.getID(), group.getID());
+ assertNull(groupResource.adaptTo(User.class));
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ User user = userResource.adaptTo(User.class);
+ assertNotNull(user);
+ assertEquals(user1.getID(), user.getID());
+ assertNull(userResource.adaptTo(Group.class));
+ }
+ }
+
+ /**
+ * For code coverage, test some adaption that falls through to the super class impl
+ */
+ @Test
+ public void adaptResourceToSomethingElse() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ NestedAuthorizableResourcesIT groupObj = groupResource.adaptTo(NestedAuthorizableResourcesIT.class);
+ assertNull(groupObj);
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ NestedAuthorizableResourcesIT userObj = userResource.adaptTo(NestedAuthorizableResourcesIT.class);
+ assertNull(userObj);
+ }
+ }
+
}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/AuthorizableValueMapIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/AuthorizableValueMapIT.java
new file mode 100644
index 0000000..3814593
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/AuthorizableValueMapIT.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jackrabbit.usermanager.it.resource;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ValueMap;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+/**
+ * Basic test of AuthorizableValueMap
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class AuthorizableValueMapIT extends BaseAuthorizableValueMapIT {
+
+ protected Group group2;
+
+ @Override
+ public void setup() throws RepositoryException, LoginException {
+ super.setup();
+
+ Map<String, Object> groupProps = new HashMap<>();
+ groupProps.put(":member", group1.getID());
+ group2 = createGroup.createGroup(adminSession, createUniqueName("group"),
+ groupProps, new ArrayList<>());
+ assertNotNull("Expected group2 to not be null", group2);
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+
+ }
+
+ @Override
+ public void teardown() {
+ try {
+ adminSession.refresh(false);
+ if (group2 != null) {
+ deleteGroup.deleteGroup(adminSession, group2.getID(), new ArrayList<>());
+ }
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+ } catch (RepositoryException e) {
+ logger.warn(String.format("Failed to delete group: %s", e.getMessage()), e);
+ }
+
+ super.teardown();
+ }
+
+ @Test
+ @Override
+ public void testSize() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ int size = vm.size();
+ assertEquals(30, size);
+
+ ValueMap vm2 = getValueMap(group1);
+ int size2 = vm2.size();
+ assertEquals(32, size2);
+ }
+
+ @Test
+ public void testUserPath() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(user1);
+ String path = vm.get("path", String.class);
+ assertEquals(user1.getPath(), path);
+ }
+
+ @Test
+ public void testGroupPath() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(group1);
+ String path = vm.get("path", String.class);
+ assertEquals(group1.getPath(), path);
+ }
+
+ @Test
+ public void testUserMemberOf() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(user1);
+ String[] memberOf = vm.get("memberOf", String[].class);
+ assertNotNull(memberOf);
+ assertArrayEquals(new String[] { String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()), String.format("%s%s", userManagerPaths.getGroupPrefix(), group2.getID()) }, memberOf);
+ }
+
+ @Test
+ public void testUserDeclaredMemberOf() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(user1);
+ String[] declaredMemberOf = vm.get("declaredMemberOf", String[].class);
+ assertNotNull(declaredMemberOf);
+ assertArrayEquals(new String[] { String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()) }, declaredMemberOf);
+ }
+
+ @Test
+ public void testUserMembers() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(user1);
+ assertNull(vm.get("members", String[].class));
+ }
+
+ @Test
+ public void testUserDeclaredMembers() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(user1);
+ assertNull(vm.get("declaredMembers", String[].class));
+ }
+
+ @Test
+ public void testGroupMemberOf() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(group1);
+ String[] memberOf = vm.get("memberOf", String[].class);
+ assertNotNull(memberOf);
+ assertArrayEquals(new String[] { String.format("%s%s", userManagerPaths.getGroupPrefix(), group2.getID()) }, memberOf);
+
+ ValueMap vm2 = getValueMap(group2);
+ String[] memberOf2 = vm2.get("memberOf", String[].class);
+ assertNotNull(memberOf2);
+ assertArrayEquals(new String[0], memberOf2);
+ }
+
+ @Test
+ public void testGroupDeclaredMemberOf() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(group1);
+ String[] declaredMemberOf = vm.get("declaredMemberOf", String[].class);
+ assertNotNull(declaredMemberOf);
+ assertArrayEquals(new String[] { String.format("%s%s", userManagerPaths.getGroupPrefix(), group2.getID())}, declaredMemberOf);
+
+ ValueMap vm2 = getValueMap(group2);
+ String[] declaredMemberOf2 = vm2.get("declaredMemberOf", String[].class);
+ assertNotNull(declaredMemberOf2);
+ assertArrayEquals(new String[0], declaredMemberOf2);
+ }
+
+ @Test
+ public void testGroupMembers() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(group1);
+ String[] members = vm.get("members", String[].class);
+ assertNotNull(members);
+ assertArrayEquals(new String[] { String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()) }, members);
+
+ ValueMap vm2 = getValueMap(group2);
+ String[] members2 = vm2.get("members", String[].class);
+ assertNotNull(members2);
+ assertArrayEquals(new String[] { String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()), String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()) }, members2);
+ }
+
+ @Test
+ public void testGroupDeclaredMembers() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(group1);
+ String[] declaredMembers = vm.get("declaredMembers", String[].class);
+ assertNotNull(declaredMembers);
+ assertArrayEquals(new String[] { String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()) }, declaredMembers);
+
+ ValueMap vm2 = getValueMap(group2);
+ String[] declaredMembers2 = vm2.get("declaredMembers", String[].class);
+ assertNotNull(declaredMembers2);
+ assertArrayEquals(new String[] { String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()) }, declaredMembers2);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/BaseAuthorizableResourcesIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/BaseAuthorizableResourcesIT.java
new file mode 100644
index 0000000..eb5b820
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/BaseAuthorizableResourcesIT.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jackrabbit.usermanager.it.resource;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.inject.Inject;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.jackrabbit.usermanager.CreateGroup;
+import org.apache.sling.jackrabbit.usermanager.CreateUser;
+import org.apache.sling.jackrabbit.usermanager.DeleteGroup;
+import org.apache.sling.jackrabbit.usermanager.DeleteUser;
+import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.jackrabbit.usermanager.it.UserManagerTestSupport;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Basic test of AuthorizableResourceProvider component
+ */
+public abstract class BaseAuthorizableResourcesIT extends UserManagerTestSupport {
+ private static AtomicLong counter = new AtomicLong(0);
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ @Inject
+ protected BundleContext bundleContext;
+
+ @Inject
+ protected SlingRepository repository;
+
+ @Inject
+ protected ResourceResolverFactory resourceResolverFactory;
+
+ @Inject
+ protected ConfigurationAdmin configAdmin;
+
+ @Inject
+ protected CreateUser createUser;
+
+ @Inject
+ protected CreateGroup createGroup;
+
+ @Inject
+ protected DeleteUser deleteUser;
+
+ @Inject
+ protected DeleteGroup deleteGroup;
+
+ @Inject
+ protected SystemUserManagerPaths userManagerPaths;
+
+ @Rule
+ public TestName testName = new TestName();
+
+ protected Session adminSession;
+ protected User user1;
+ protected Group group1;
+
+ @Before
+ public void setup() throws RepositoryException, LoginException {
+ adminSession = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
+ assertNotNull("Expected adminSession to not be null", adminSession);
+ }
+
+ @After
+ public void teardown() {
+ try {
+ adminSession.refresh(false);
+ if (user1 != null) {
+ deleteUser.deleteUser(adminSession, user1.getID(), new ArrayList<>());
+ }
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+ } catch (RepositoryException e) {
+ logger.warn(String.format("Failed to delete user: %s", e.getMessage()), e);
+ }
+ try {
+ adminSession.refresh(false);
+ if (group1 != null) {
+ deleteGroup.deleteGroup(adminSession, group1.getID(), new ArrayList<>());
+ }
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+ } catch (RepositoryException e) {
+ logger.warn(String.format("Failed to delete group: %s", e.getMessage()), e);
+ }
+
+ adminSession.logout();
+ }
+
+ protected String createUniqueName(String prefix) {
+ return String.format("%s_%s%d", prefix, testName.getMethodName(), counter.incrementAndGet());
+ }
+
+ protected void createResourcesForAdaptTo() throws RepositoryException {
+ Map<String, Object> nestedProps = new HashMap<>();
+ nestedProps.put("key1", "value1");
+ nestedProps.put("private/key2", "value2");
+ nestedProps.put("private/sub/key3", "value3");
+
+ group1 = createGroup.createGroup(adminSession, createUniqueName("group"),
+ nestedProps, new ArrayList<>());
+ assertNotNull("Expected group1 to not be null", group1);
+
+ user1 = createUser.createUser(adminSession, createUniqueName("user"), "testPwd", "testPwd",
+ nestedProps, new ArrayList<>());
+ assertNotNull("Expected user1 to not be null", user1);
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/BaseAuthorizableValueMapIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/BaseAuthorizableValueMapIT.java
new file mode 100644
index 0000000..69d625d
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/BaseAuthorizableValueMapIT.java
@@ -0,0 +1,759 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jackrabbit.usermanager.it.resource;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.jcr.Binary;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.util.ISO8601;
+import org.apache.jackrabbit.value.ValueFactoryImpl;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.api.JcrResourceConstants;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Basic test of AuthorizableValueMap
+ */
+public abstract class BaseAuthorizableValueMapIT extends BaseAuthorizableResourcesIT {
+
+ protected Calendar NOW = Calendar.getInstance();
+ protected String uuid;
+ protected String uuid2;
+
+ @Before
+ public void setup() throws RepositoryException, LoginException {
+ super.setup();
+
+ // clear out the milliseconds field which isn't
+ // relevant for date property values
+ NOW.set(Calendar.MILLISECOND, 0);
+
+ uuid = UUID.randomUUID().toString();
+ uuid2 = UUID.randomUUID().toString();
+
+ Map<String, Object> props = createAuthorizableProps();
+
+ user1 = createUser.createUser(adminSession, createUniqueName("user"), "testPwd", "testPwd",
+ props, new ArrayList<>());
+ assertNotNull("Expected user1 to not be null", user1);
+
+ Map<String, Object> groupProps = new HashMap<>(props);
+ groupProps.put(":member", user1.getID());
+ group1 = createGroup.createGroup(adminSession, createUniqueName("group"),
+ groupProps, new ArrayList<>());
+ assertNotNull("Expected group1 to not be null", group1);
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+ }
+
+ protected Map<String, Object> createAuthorizableProps() throws LoginException {
+ return createAuthorizableProps("");
+ }
+ protected Map<String, Object> createAuthorizableProps(String prefix) throws LoginException {
+ Map<String, Object> props = new HashMap<>();
+ props.put(String.format("%skey1", prefix), "value1");
+
+ props.put(String.format("%sstring1@TypeHint", prefix), PropertyType.TYPENAME_STRING);
+ props.put(String.format("%sstring1", prefix), "value1");
+
+ props.put(String.format("%sstring2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_STRING));
+ props.put(String.format("%sstring2", prefix), new String[] {"value1", "value2"});
+
+ props.put(String.format("%sbinary1@TypeHint", prefix), PropertyType.TYPENAME_BINARY);
+ props.put(String.format("%sbinary1", prefix), "value1");
+
+ props.put(String.format("%sbinary2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_BINARY));
+ props.put(String.format("%sbinary2", prefix), new String[] {"value1", "value2"});
+
+ props.put(String.format("%sboolean1@TypeHint", prefix), PropertyType.TYPENAME_BOOLEAN);
+ props.put(String.format("%sboolean1", prefix), "false");
+
+ props.put(String.format("%sboolean2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_BOOLEAN));
+ props.put(String.format("%sboolean2", prefix), new String[] {"false", "true"});
+
+ props.put(String.format("%slong1@TypeHint", prefix), PropertyType.TYPENAME_LONG);
+ props.put(String.format("%slong1", prefix), "1");
+
+ props.put(String.format("%slong2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_LONG));
+ props.put(String.format("%slong2", prefix), new String[] {"1", "2"});
+
+ props.put(String.format("%sdouble1@TypeHint", prefix), PropertyType.TYPENAME_DOUBLE);
+ props.put(String.format("%sdouble1", prefix), "1.1");
+
+ props.put(String.format("%sdouble2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_DOUBLE));
+ props.put(String.format("%sdouble2", prefix), new String[] {"1.1", "2.2"});
+
+ props.put(String.format("%sdecimal1@TypeHint", prefix), PropertyType.TYPENAME_DECIMAL);
+ props.put(String.format("%sdecimal1", prefix), "1");
+
+ props.put(String.format("%sdecimal2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_DECIMAL));
+ props.put(String.format("%sdecimal2", prefix), new String[] {"1", "2"});
+
+ props.put(String.format("%sdate1@TypeHint", prefix), PropertyType.TYPENAME_DATE);
+ props.put(String.format("%sdate1", prefix), ISO8601.format(NOW));
+
+ props.put(String.format("%sdate2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_DATE));
+ props.put(String.format("%sdate2", prefix), new String[] {ISO8601.format(NOW), ISO8601.format(NOW)});
+
+ props.put(String.format("%sname1@TypeHint", prefix), PropertyType.TYPENAME_NAME);
+ props.put(String.format("%sname1", prefix), "name1");
+
+ props.put(String.format("%sname2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_NAME));
+ props.put(String.format("%sname2", prefix), new String[] {"name1", "name2"});
+
+ props.put(String.format("%spath1@TypeHint", prefix), PropertyType.TYPENAME_PATH);
+ props.put(String.format("%spath1", prefix), "/content");
+
+ props.put(String.format("%spath2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_PATH));
+ props.put(String.format("%spath2", prefix), new String [] {"/content", "/home"});
+
+ props.put(String.format("%sreference1@TypeHint", prefix), PropertyType.TYPENAME_REFERENCE);
+ props.put(String.format("%sreference1", prefix), uuid);
+
+ props.put(String.format("%sreference2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_REFERENCE));
+ props.put(String.format("%sreference2", prefix), new String[] {uuid, uuid});
+
+ props.put(String.format("%sweakreference1@TypeHint", prefix), PropertyType.TYPENAME_WEAKREFERENCE);
+ props.put(String.format("%sweakreference1", prefix), uuid);
+
+ props.put(String.format("%sweakreference2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_WEAKREFERENCE));
+ props.put(String.format("%sweakreference2", prefix), new String[] {uuid, uuid});
+
+ props.put(String.format("%suri1@TypeHint", prefix), PropertyType.TYPENAME_URI);
+ props.put(String.format("%suri1", prefix), "http://localhost:8080/content");
+
+ props.put(String.format("%suri2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_URI));
+ props.put(String.format("%suri2", prefix), new String [] {"http://localhost:8080/content", "http://localhost:8080/home"});
+
+ props.put(String.format("%sundefined1@TypeHint", prefix), PropertyType.TYPENAME_UNDEFINED);
+ props.put(String.format("%sundefined1", prefix), "value1");
+
+ props.put(String.format("%sundefined2@TypeHint", prefix), toMultivalueTypeHint(PropertyType.TYPENAME_UNDEFINED));
+ props.put(String.format("%sundefined2", prefix), new String [] {"value1", "value2"});
+
+ return props;
+ }
+
+ protected String toMultivalueTypeHint(String typeNameString) {
+ return String.format("%s[]", typeNameString);
+ }
+
+ @Test
+ public void testGetStringClassOfT() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(user1);
+ String vmValue1 = vm.get("key1", String.class);
+ assertEquals("value1", vmValue1);
+
+ Long vmValue2 = vm.get("key1", Long.class);
+ assertNull(vmValue2);
+
+ Object vmValue3 = vm.get("key1", (Class<?>)null);
+ assertEquals("value1", vmValue3);
+
+ String vmValue4 = vm.get("not_a_key1", String.class);
+ assertNull(vmValue4);
+
+ ValueMap vm2 = getValueMap(group1);
+ String vm2Value1 = vm2.get("key1", String.class);
+ assertEquals("value1", vm2Value1);
+
+ Long vm2Value2 = vm2.get("key1", Long.class);
+ assertNull(vm2Value2);
+
+ Object vm2Value3 = vm2.get("key1", (Class<?>)null);
+ assertEquals("value1", vm2Value3);
+
+ String vm2Value4 = vm2.get("not_a_key1", String.class);
+ assertNull(vm2Value4);
+
+
+ assertEquals("value1", vm.get("string1", String.class));
+ assertArrayEquals(new String[] {"value1", "value2"}, vm.get("string2", String[].class));
+
+ // try converting single value to array
+ assertArrayEquals(new String[] {"value1"}, vm.get("string1", String[].class));
+ // try converting multi value to first item
+ assertEquals("value1", vm.get("string2", String.class));
+ // try some other type that has no conversion available
+ assertNull(vm.get("string1", Object.class));
+ assertNull(vm.get("string2", Object[].class));
+
+ try (InputStream binary1 = vm.get("binary1", InputStream.class)) {
+ String binary1asString = IOUtils.toString(binary1, StandardCharsets.UTF_8);
+ assertEquals("value1", binary1asString);
+ }
+ InputStream[] binary2AsStream = vm.get("binary2", InputStream[].class);
+ assertEquals(2, binary2AsStream.length);
+ try (InputStream is = binary2AsStream[0]) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value1", binary2asString);
+ }
+ try (InputStream is = binary2AsStream[1]) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value2", binary2asString);
+ }
+
+ Binary binary1 = vm.get("binary1", Binary.class);
+ try (InputStream is = binary1.getStream()) {
+ String binary1asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value1", binary1asString);
+ }
+ Binary[] binary2AsBinary = vm.get("binary2", Binary[].class);
+ assertEquals(2, binary2AsBinary.length);
+ try (InputStream is = binary2AsBinary[0].getStream()) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value1", binary2asString);
+ }
+ try (InputStream is = binary2AsBinary[1].getStream()) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value2", binary2asString);
+ }
+
+ assertEquals(Boolean.FALSE, vm.get("boolean1", Boolean.class));
+ assertArrayEquals(new Boolean[] {Boolean.FALSE, Boolean.TRUE}, vm.get("boolean2", Boolean[].class));
+
+ assertEquals(Long.valueOf(1), vm.get("long1", Long.class));
+ assertArrayEquals(new Long[] {1L, 2L}, vm.get("long2", Long[].class));
+
+ // other types of numbers
+ assertEquals(Short.valueOf((short)1), vm.get("long1", Short.class));
+ assertArrayEquals(new Short[] {1, 2}, vm.get("long2", Short[].class));
+ assertEquals(Integer.valueOf(1), vm.get("long1", Integer.class));
+ assertArrayEquals(new Integer[] {1, 2}, vm.get("long2", Integer[].class));
+ assertEquals(Byte.valueOf((byte)1), vm.get("long1", Byte.class));
+ assertArrayEquals(new Byte[] {1, 2}, vm.get("long2", Byte[].class));
+
+ assertEquals(Double.valueOf(1.1), vm.get("double1", Double.class));
+ assertArrayEquals(new Double[] {1.1, 2.2}, vm.get("double2", Double[].class));
+
+ // other types of numbers
+ assertEquals(Float.valueOf((float)1.1), vm.get("double1", Float.class));
+ assertArrayEquals(new Float[] {(float)1.1, (float)2.2}, vm.get("double2", Float[].class));
+
+ assertEquals(new BigDecimal(1), vm.get("decimal1", BigDecimal.class));
+ assertArrayEquals(new BigDecimal[] {new BigDecimal(1), new BigDecimal(2)}, vm.get("decimal2", BigDecimal[].class));
+
+ Calendar date1 = vm.get("date1", Calendar.class);
+ assertEquals(ISO8601.format(NOW), ISO8601.format(date1));
+ Calendar[] date2 = vm.get("date2", Calendar[].class);
+ assertEquals(2, date2.length);
+ assertEquals(ISO8601.format(NOW), ISO8601.format(date2[0]));
+ assertEquals(ISO8601.format(NOW), ISO8601.format(date2[1]));
+
+ // other types of date
+ assertEquals(NOW.getTime(), vm.get("date1", Date.class));
+ assertArrayEquals(new Date[] {NOW.getTime(), NOW.getTime()}, vm.get("date2", Date[].class));
+
+ ValueFactory valueFactory = ValueFactoryImpl.getInstance();
+ assertValueEquals(valueFactory.createValue("name1", PropertyType.NAME), vm.get("name1", Value.class));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue("name1", PropertyType.NAME), valueFactory.createValue("name2", PropertyType.NAME)}, vm.get("name2", Value[].class));
+
+ assertValueEquals(valueFactory.createValue("/content", PropertyType.PATH), vm.get("path1", Value.class));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue("/content", PropertyType.PATH), valueFactory.createValue("/home", PropertyType.PATH)}, vm.get("path2", Value[].class));
+
+ assertValueEquals(valueFactory.createValue(uuid, PropertyType.REFERENCE), vm.get("reference1", Value.class));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue(uuid, PropertyType.REFERENCE), valueFactory.createValue(uuid, PropertyType.REFERENCE)}, vm.get("reference2", Value[].class));
+
+ assertValueEquals(valueFactory.createValue(uuid, PropertyType.WEAKREFERENCE), vm.get("weakreference1", Value.class));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue(uuid, PropertyType.WEAKREFERENCE), valueFactory.createValue(uuid, PropertyType.WEAKREFERENCE)}, vm.get("weakreference2", Value[].class));
+
+ assertValueEquals(valueFactory.createValue("http://localhost:8080/content", PropertyType.URI), vm.get("uri1", Value.class));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue("http://localhost:8080/content", PropertyType.URI), valueFactory.createValue("http://localhost:8080/home", PropertyType.URI)}, vm.get("uri2", Value[].class));
+
+ assertEquals("value1", vm.get("undefined1", String.class));
+ assertArrayEquals(new String[] {"value1", "value2"}, vm.get("undefined2", String[].class));
+ }
+
+ @Test
+ public void testGetStringT() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(user1);
+ String vmValue1 = vm.get("key1", "default1");
+ assertEquals("value1", vmValue1);
+ String vmValue2 = vm.get("not_a_key1", "default1");
+ assertEquals("default1", vmValue2);
+ String vmValue3 = vm.get("key1", (String)null);
+ assertEquals("value1", vmValue3);
+ String vmValue4 = vm.get("not_a_key1", (String)null);
+ assertNull(vmValue4);
+
+ ValueMap vm2 = getValueMap(group1);
+ String vm2Value1 = vm2.get("key1", "default1");
+ assertEquals("value1", vm2Value1);
+ String vm2Value2 = vm2.get("not_a_key1", "default1");
+ assertEquals("default1", vm2Value2);
+ String vm2Value3 = vm2.get("key1", (String)null);
+ assertEquals("value1", vm2Value3);
+ String vm2Value4 = vm2.get("not_a_key1", (String)null);
+ assertNull(vm2Value4);
+
+
+ assertEquals("value1", vm.get("string1", "default1"));
+ assertEquals("default1", vm.get("string1a", "default1"));
+ assertArrayEquals(new String[] {"value1", "value2"}, vm.get("string2", new String[] {"default1", "default2"}));
+ assertArrayEquals(new String[] {"default1", "default2"}, vm.get("string2a", new String[] {"default1", "default2"}));
+
+ // try converting single value to array
+ assertArrayEquals(new String[] {"value1"}, vm.get("string1", new String[] {"default1"}));
+ assertArrayEquals(new String[] {"default1"}, vm.get("string1a", new String[] {"default1"}));
+ // try converting multi value to first item
+ assertEquals("value1", vm.get("string2", "default1"));
+ assertEquals("default1", vm.get("string2a", "default1"));
+ // try some other type that has no conversion available
+ Object defaultObj = new Object();
+ assertEquals(defaultObj, vm.get("string1", defaultObj));
+ Object[] defaultObjArray = new Object[] {defaultObj};
+ assertArrayEquals(defaultObjArray, vm.get("string2", defaultObjArray));
+
+ try (InputStream defaultIS = new ByteArrayInputStream("default1".getBytes());
+ InputStream binary1 = vm.get("binary1", defaultIS)) {
+ String binary1asString = IOUtils.toString(binary1, StandardCharsets.UTF_8);
+ assertEquals("value1", binary1asString);
+ }
+ try (InputStream defaultIS = new ByteArrayInputStream("default1".getBytes());
+ InputStream binary1 = vm.get("binary1a", defaultIS)) {
+ String binary1asString = IOUtils.toString(binary1, StandardCharsets.UTF_8);
+ assertEquals("default1", binary1asString);
+ }
+ try (InputStream defaultIS1 = new ByteArrayInputStream("default1".getBytes());
+ InputStream defaultIS2 = new ByteArrayInputStream("default2".getBytes());) {
+ InputStream[] binary2AsStream = vm.get("binary2", new InputStream[] {defaultIS1, defaultIS2});
+ assertEquals(2, binary2AsStream.length);
+ try (InputStream is = binary2AsStream[0]) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value1", binary2asString);
+ }
+ try (InputStream is = binary2AsStream[1]) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value2", binary2asString);
+ }
+ }
+ try (InputStream defaultIS1 = new ByteArrayInputStream("default1".getBytes());
+ InputStream defaultIS2 = new ByteArrayInputStream("default2".getBytes());) {
+ InputStream[] binary2AsStream = vm.get("binary2a", new InputStream[] {defaultIS1, defaultIS2});
+ assertEquals(2, binary2AsStream.length);
+ try (InputStream is = binary2AsStream[0]) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("default1", binary2asString);
+ }
+ try (InputStream is = binary2AsStream[1]) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("default2", binary2asString);
+ }
+ }
+
+ ValueFactory valueFactory = ValueFactoryImpl.getInstance();
+ Binary defaultBinary1;
+ try (InputStream is = new ByteArrayInputStream("default1".getBytes())) {
+ defaultBinary1 = valueFactory.createBinary(is);
+ }
+ Binary defaultBinary2;
+ try (InputStream is = new ByteArrayInputStream("default2".getBytes())) {
+ defaultBinary2 = valueFactory.createBinary(is);
+ }
+
+ Binary binary1 = vm.get("binary1", defaultBinary1);
+ try (InputStream is = binary1.getStream()) {
+ String binary1asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value1", binary1asString);
+ }
+ Binary binary1a = vm.get("binary1a", defaultBinary1);
+ try (InputStream is = binary1a.getStream()) {
+ String binary1asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("default1", binary1asString);
+ }
+ Binary[] binary2AsBinary = vm.get("binary2", new Binary[] {defaultBinary1, defaultBinary2});
+ assertEquals(2, binary2AsBinary.length);
+ try (InputStream is = binary2AsBinary[0].getStream()) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value1", binary2asString);
+ }
+ try (InputStream is = binary2AsBinary[1].getStream()) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value2", binary2asString);
+ }
+ Binary[] binary2aAsBinary = vm.get("binary2a", new Binary[] {defaultBinary1, defaultBinary2});
+ assertEquals(2, binary2aAsBinary.length);
+ try (InputStream is = binary2aAsBinary[0].getStream()) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("default1", binary2asString);
+ }
+ try (InputStream is = binary2aAsBinary[1].getStream()) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("default2", binary2asString);
+ }
+
+ assertEquals(Boolean.FALSE, vm.get("boolean1", Boolean.TRUE));
+ assertEquals(Boolean.TRUE, vm.get("boolean1a", Boolean.TRUE));
+ assertArrayEquals(new Boolean[] {Boolean.FALSE, Boolean.TRUE}, vm.get("boolean2", new Boolean[] {true, true}));
+ assertArrayEquals(new Boolean[] {Boolean.TRUE, Boolean.TRUE}, vm.get("boolean2a", new Boolean[] {true, true}));
+
+ assertEquals(Long.valueOf(1), vm.get("long1", 2L));
+ assertEquals(Long.valueOf(2), vm.get("long1a", 2L));
+ assertArrayEquals(new Long[] {1L, 2L}, vm.get("long2", new Long[] {1L, 1L}));
+ assertArrayEquals(new Long[] {1L, 1L}, vm.get("long2a", new Long[] {1L, 1L}));
+
+ // other types of numbers
+ assertEquals(Short.valueOf((short)1), vm.get("long1",(short)2));
+ assertEquals(Short.valueOf((short)2), vm.get("long1a",(short)2));
+ assertArrayEquals(new Short[] {1, 2}, vm.get("long2", new Short[] {1, 1}));
+ assertArrayEquals(new Short[] {1, 1}, vm.get("long2a", new Short[] {1, 1}));
+
+ assertEquals(Integer.valueOf(1), vm.get("long1", (int)2));
+ assertEquals(Integer.valueOf(2), vm.get("long1a", (int)2));
+ assertArrayEquals(new Integer[] {1, 2}, vm.get("long2", new Integer[] {1, 1}));
+ assertArrayEquals(new Integer[] {1, 1}, vm.get("long2a", new Integer[] {1, 1}));
+
+ assertEquals(Byte.valueOf((byte)1), vm.get("long1", (byte)2));
+ assertEquals(Byte.valueOf((byte)2), vm.get("long1a", (byte)2));
+ assertArrayEquals(new Byte[] {1, 2}, vm.get("long2", new Byte[] {1, 1}));
+ assertArrayEquals(new Byte[] {1, 1}, vm.get("long2a", new Byte[] {1, 1}));
+
+ assertEquals(Double.valueOf(1.1), vm.get("double1", 2.2));
+ assertEquals(Double.valueOf(2.2), vm.get("double1a", 2.2));
+ assertArrayEquals(new Double[] {1.1, 2.2}, vm.get("double2", new Double[] {1.1, 1.1}));
+ assertArrayEquals(new Double[] {1.1, 1.1}, vm.get("double2a", new Double[] {1.1, 1.1}));
+
+ // other types of numbers
+ assertEquals(Float.valueOf((float)1.1), vm.get("double1", (float)2.2));
+ assertEquals(Float.valueOf((float)2.2), vm.get("double1a", (float)2.2));
+ assertArrayEquals(new Float[] {(float)1.1, (float)2.2}, vm.get("double2", new Float[] {(float)1.1, (float)1.1}));
+ assertArrayEquals(new Float[] {(float)1.1, (float)1.1}, vm.get("double2a", new Float[] {(float)1.1, (float)1.1}));
+
+ assertEquals(new BigDecimal(1), vm.get("decimal1", new BigDecimal(2)));
+ assertEquals(new BigDecimal(2), vm.get("decimal1a", new BigDecimal(2)));
+ assertArrayEquals(new BigDecimal[] {new BigDecimal(1), new BigDecimal(2)}, vm.get("decimal2", new BigDecimal[] { new BigDecimal(1), new BigDecimal(1)}));
+ assertArrayEquals(new BigDecimal[] {new BigDecimal(1), new BigDecimal(1)}, vm.get("decimal2a", new BigDecimal[] { new BigDecimal(1), new BigDecimal(1)}));
+
+ Calendar date1 = vm.get("date1", NOW);
+ assertEquals(ISO8601.format(NOW), ISO8601.format(date1));
+ Calendar date1a = vm.get("date1a", NOW);
+ assertEquals(ISO8601.format(NOW), ISO8601.format(date1a));
+ Calendar[] date2 = vm.get("date2", new Calendar[] {NOW, NOW});
+ assertEquals(2, date2.length);
+ assertEquals(ISO8601.format(NOW), ISO8601.format(date2[0]));
+ assertEquals(ISO8601.format(NOW), ISO8601.format(date2[1]));
+ Calendar[] date2a = vm.get("date2a", new Calendar[] {NOW, NOW});
+ assertEquals(2, date2a.length);
+ assertEquals(ISO8601.format(NOW), ISO8601.format(date2a[0]));
+ assertEquals(ISO8601.format(NOW), ISO8601.format(date2a[1]));
+
+ // other types of date
+ assertEquals(NOW.getTime(), vm.get("date1", NOW.getTime()));
+ assertEquals(NOW.getTime(), vm.get("date1a", NOW.getTime()));
+ assertArrayEquals(new Date[] {NOW.getTime(), NOW.getTime()}, vm.get("date2", new Date[] {NOW.getTime(), NOW.getTime()}));
+ assertArrayEquals(new Date[] {NOW.getTime(), NOW.getTime()}, vm.get("date2a", new Date[] {NOW.getTime(), NOW.getTime()}));
+
+ assertValueEquals(valueFactory.createValue("name1", PropertyType.NAME), vm.get("name1", valueFactory.createValue("name2", PropertyType.NAME)));
+ assertValueEquals(valueFactory.createValue("name2", PropertyType.NAME), vm.get("name1a", valueFactory.createValue("name2", PropertyType.NAME)));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue("name1", PropertyType.NAME), valueFactory.createValue("name2", PropertyType.NAME)}, vm.get("name2", new Value[] {valueFactory.createValue("name1", PropertyType.NAME), valueFactory.createValue("name3", PropertyType.NAME)}));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue("name1", PropertyType.NAME), valueFactory.createValue("name3", PropertyType.NAME)}, vm.get("name2a", new Value[] {valueFactory.createValue("name1", PropertyType.NAME), valueFactory.createValue("name3", PropertyType.NAME)}));
+
+ assertValueEquals(valueFactory.createValue("/content", PropertyType.PATH), vm.get("path1", valueFactory.createValue("/content2", PropertyType.PATH)));
+ assertValueEquals(valueFactory.createValue("/content2", PropertyType.PATH), vm.get("path1a", valueFactory.createValue("/content2", PropertyType.PATH)));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue("/content", PropertyType.PATH), valueFactory.createValue("/home", PropertyType.PATH)}, vm.get("path2", new Value[] {valueFactory.createValue("/content2", PropertyType.PATH), valueFactory.createValue("/home2", PropertyType.PATH)}));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue("/content2", PropertyType.PATH), valueFactory.createValue("/home2", PropertyType.PATH)}, vm.get("path2a", new Value[] {valueFactory.createValue("/content2", PropertyType.PATH), valueFactory.createValue("/home2", PropertyType.PATH)}));
+
+ assertValueEquals(valueFactory.createValue(uuid, PropertyType.REFERENCE), vm.get("reference1", valueFactory.createValue(uuid2, PropertyType.REFERENCE)));
+ assertValueEquals(valueFactory.createValue(uuid2, PropertyType.REFERENCE), vm.get("reference1a", valueFactory.createValue(uuid2, PropertyType.REFERENCE)));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue(uuid, PropertyType.REFERENCE), valueFactory.createValue(uuid, PropertyType.REFERENCE)}, vm.get("reference2", new Value[] {valueFactory.createValue(uuid2, PropertyType.REFERENCE), valueFactory.createValue(uuid2, PropertyType.REFERENCE)}));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue(uuid2, PropertyType.REFERENCE), valueFactory.createValue(uuid2, PropertyType.REFERENCE)}, vm.get("reference2a", new Value[] {valueFactory.createValue(uuid2, PropertyType.REFERENCE), valueFactory.createValue(uuid2, PropertyType.REFERENCE)}));
+
+ assertValueEquals(valueFactory.createValue(uuid, PropertyType.WEAKREFERENCE), vm.get("weakreference1", valueFactory.createValue(uuid2, PropertyType.WEAKREFERENCE)));
+ assertValueEquals(valueFactory.createValue(uuid2, PropertyType.WEAKREFERENCE), vm.get("weakreference1a", valueFactory.createValue(uuid2, PropertyType.WEAKREFERENCE)));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue(uuid, PropertyType.WEAKREFERENCE), valueFactory.createValue(uuid, PropertyType.WEAKREFERENCE)}, vm.get("weakreference2", new Value[] {valueFactory.createValue(uuid2, PropertyType.WEAKREFERENCE), valueFactory.createValue(uuid2, PropertyType.WEAKREFERENCE)}));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue(uuid2, PropertyType.WEAKREFERENCE), valueFactory.createValue(uuid2, PropertyType.WEAKREFERENCE)}, vm.get("weakreference2a", new Value[] {valueFactory.createValue(uuid2, PropertyType.WEAKREFERENCE), valueFactory.createValue(uuid2, PropertyType.WEAKREFERENCE)}));
+
+ assertValueEquals(valueFactory.createValue("http://localhost:8080/content", PropertyType.URI), vm.get("uri1", valueFactory.createValue("http://localhost:8080/content2", PropertyType.URI)));
+ assertValueEquals(valueFactory.createValue("http://localhost:8080/content2", PropertyType.URI), vm.get("uri1a", valueFactory.createValue("http://localhost:8080/content2", PropertyType.URI)));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue("http://localhost:8080/content", PropertyType.URI), valueFactory.createValue("http://localhost:8080/home", PropertyType.URI)}, vm.get("uri2",new Value[] {valueFactory.createValue("http://localhost:8080/content2", PropertyType.URI), valueFactory.createValue("http://localhost:8080/home2", PropertyType.URI)}));
+ assertValueArrayEquals(new Value[] {valueFactory.createValue("http://localhost:8080/content2", PropertyType.URI), valueFactory.createValue("http://localhost:8080/home2", PropertyType.URI)}, vm.get("uri2a",new Value[] {valueFactory.createValue("http://localhost:8080/content2", PropertyType.URI), valueFactory.createValue("http://localhost:8080/home2", PropertyType.URI)}));
+
+ assertEquals("value1", vm.get("undefined1", "default1"));
+ assertEquals("default1", vm.get("undefined1a", "default1"));
+ assertArrayEquals(new String[] {"value1", "value2"}, vm.get("undefined2", new String[] {"default1", "default2"}));
+ assertArrayEquals(new String[] {"default1", "default2"}, vm.get("undefined2a", new String[] {"default1", "default2"}));
+ }
+
+ @Test
+ public void testContainsKey() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ assertTrue(vm.containsKey("key1"));
+ assertFalse(vm.containsKey("not_a_key1"));
+
+ ValueMap vm2 = getValueMap(group1);
+ assertTrue(vm2.containsKey("key1"));
+ assertFalse(vm2.containsKey("not_a_key1"));
+ }
+
+ @Test
+ public void testContainsValue() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ assertTrue(vm.containsValue("value1"));
+ assertFalse(vm.containsValue("not_a_value1"));
+
+ ValueMap vm2 = getValueMap(group1);
+ assertTrue(vm2.containsValue("value1"));
+ assertFalse(vm2.containsValue("not_a_value1"));
+ }
+
+ @Test
+ public void testEntrySet() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ Set<Entry<String, Object>> entrySet = vm.entrySet();
+ assertNotNull(entrySet);
+
+ ValueMap vm2 = getValueMap(group1);
+ Set<Entry<String, Object>> entrySet2 = vm2.entrySet();
+ assertNotNull(entrySet2);
+ }
+
+ @Test
+ public void testGetObject() throws LoginException, RepositoryException, IOException {
+ ValueMap vm = getValueMap(user1);
+ Object vmValue1 = vm.get("key1");
+ assertEquals("value1", vmValue1);
+ Object vmValue2 = vm.get("not_a_key1");
+ assertNull(vmValue2);
+ //read again to cover the cached state
+ Object vmValue3 = vm.get("key1");
+ assertEquals("value1", vmValue3);
+
+ ValueMap vm2 = getValueMap(group1);
+ Object vm2Value1 = vm2.get("key1");
+ assertEquals("value1", vm2Value1);
+ Object vm2Value2 = vm2.get("not_a_key1");
+ assertNull(vm2Value2);
+ //read again to cover the cached state
+ Object vm2Value3 = vm2.get("key1");
+ assertEquals("value1", vm2Value3);
+
+
+ assertEquals("value1", vm.get("string1"));
+ assertArrayEquals(new Object[] {"value1", "value2"}, (Object[])vm.get("string2"));
+
+ Object binary1 = vm.get("binary1");
+ assertTrue(binary1 instanceof InputStream);
+ try (InputStream is = (InputStream)binary1) {
+ String binary1asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value1", binary1asString);
+ }
+ Object binary2 = vm.get("binary2");
+ assertTrue(binary2.getClass().isArray());
+ assertEquals(2, Array.getLength(binary2));
+ try (InputStream is = (InputStream)Array.get(binary2, 0)) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value1", binary2asString);
+ }
+ try (InputStream is = (InputStream)Array.get(binary2, 1)) {
+ String binary2asString = IOUtils.toString(is, StandardCharsets.UTF_8);
+ assertEquals("value2", binary2asString);
+ }
+
+ assertEquals(Boolean.FALSE, vm.get("boolean1"));
+ assertArrayEquals(new Object[] {Boolean.FALSE, Boolean.TRUE}, (Object[])vm.get("boolean2"));
+
+ assertEquals(Long.valueOf(1), vm.get("long1"));
+ assertArrayEquals(new Object[] {1L, 2L}, (Object[])vm.get("long2"));
+
+ assertEquals(Double.valueOf(1.1), vm.get("double1"));
+ assertArrayEquals(new Object[] {1.1, 2.2}, (Object[])vm.get("double2"));
+
+ assertEquals(new BigDecimal(1), vm.get("decimal1"));
+ assertArrayEquals(new Object[] {new BigDecimal(1), new BigDecimal(2)}, (Object[])vm.get("decimal2"));
+
+ Object date1 = vm.get("date1");
+ assertTrue(date1 instanceof Calendar);
+ assertEquals(ISO8601.format(NOW), ISO8601.format((Calendar)date1));
+ Object date2 = vm.get("date2");
+ assertTrue(date2.getClass().isArray());
+ assertEquals(2, Array.getLength(date2));
+ assertEquals(ISO8601.format(NOW), ISO8601.format((Calendar)Array.get(date2, 0)));
+ assertEquals(ISO8601.format(NOW), ISO8601.format((Calendar)Array.get(date2, 1)));
+
+ assertEquals("name1", vm.get("name1"));
+ assertArrayEquals(new Object[] {"name1", "name2"}, (Object[])vm.get("name2"));
+
+ assertEquals("/content", vm.get("path1"));
+ assertArrayEquals(new Object[] {"/content", "/home"}, (Object[])vm.get("path2"));
+
+ assertEquals(uuid, vm.get("reference1"));
+ assertArrayEquals(new Object[] {uuid, uuid}, (Object[])vm.get("reference2"));
+
+ assertEquals(uuid, vm.get("weakreference1"));
+ assertArrayEquals(new Object[] {uuid, uuid}, (Object[])vm.get("weakreference2"));
+
+ assertEquals("http://localhost:8080/content", vm.get("uri1"));
+ assertArrayEquals(new Object[] {"http://localhost:8080/content", "http://localhost:8080/home"}, (Object[])vm.get("uri2"));
+
+ assertEquals("value1", vm.get("undefined1"));
+ assertArrayEquals(new Object[] {"value1", "value2"}, (Object[])vm.get("undefined2"));
+ }
+
+ @Test
+ public void testKeySet() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ Set<String> keySet = vm.keySet();
+ assertNotNull(keySet);
+
+ ValueMap vm2 = getValueMap(group1);
+ Set<String> keySet2 = vm2.keySet();
+ assertNotNull(keySet2);
+ }
+
+ @Test
+ public abstract void testSize() throws LoginException, RepositoryException;
+
+ @Test
+ public void testIsEmpty() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ assertFalse(vm.isEmpty());
+
+ ValueMap vm2 = getValueMap(group1);
+ assertFalse(vm2.isEmpty());
+ }
+
+ @Test
+ public void testValues() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ Collection<Object> values = vm.values();
+ assertNotNull(values);
+
+ ValueMap vm2 = getValueMap(group1);
+ Collection<Object> values2 = vm2.values();
+ assertNotNull(values2);
+ }
+
+ @Test
+ public void testToString() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ String string = vm.toString();
+ assertNotNull(string);
+
+ ValueMap vm2 = getValueMap(group1);
+ String string2 = vm2.toString();
+ assertNotNull(string2);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testRemoveFromUser() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ vm.remove("key1");
+ }
+ @Test(expected = UnsupportedOperationException.class)
+ public void testRemoveFromGroup() throws LoginException, RepositoryException {
+ ValueMap vm2 = getValueMap(group1);
+ vm2.remove("key1");
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testClearFromUser() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ vm.clear();
+ }
+ @Test(expected = UnsupportedOperationException.class)
+ public void testClearFromGroup() throws LoginException, RepositoryException {
+ ValueMap vm2 = getValueMap(group1);
+ vm2.clear();
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testPutFromUser() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ vm.put("another", "value");
+ }
+ @Test(expected = UnsupportedOperationException.class)
+ public void testPutFromGroup() throws LoginException, RepositoryException {
+ ValueMap vm2 = getValueMap(group1);
+ vm2.put("another", "value");
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testPutAllFromUser() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ vm.putAll(Collections.singletonMap("another", "value"));
+ }
+ @Test(expected = UnsupportedOperationException.class)
+ public void testPutAllFromGroup() throws LoginException, RepositoryException {
+ ValueMap vm2 = getValueMap(group1);
+ vm2.putAll(Collections.singletonMap("another", "value"));
+ }
+
+ protected ValueMap getValueMap(Authorizable a) throws LoginException, RepositoryException {
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource;
+ if (a.isGroup()) {
+ resource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getGroupPrefix(), a.getID()));
+ } else {
+ resource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getUserPrefix(), a.getID()));
+ }
+ assertNotNull(resource);
+ ValueMap vm = resource.adaptTo(ValueMap.class);
+ assertNotNull(vm);
+ return vm;
+ }
+ }
+
+ protected void assertValueEquals(Value expected, Value actual) throws RepositoryException {
+ assertEquals(expected.getType(), actual.getType());
+ assertEquals(expected.getString(), actual.getString());
+ }
+
+ protected void assertValueArrayEquals(Value[] expected, Value[] actual) throws RepositoryException {
+ assertEquals(expected.length, actual.length);
+ for (int i=0; i < expected.length; i++) {
+ assertEquals(String.format("item %d was not equal", i), expected[i].getString(), actual[i].getString());
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/NestedAuthorizableResourcesIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/NestedAuthorizableResourcesIT.java
new file mode 100644
index 0000000..bec522e
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/NestedAuthorizableResourcesIT.java
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jackrabbit.usermanager.it.resource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.api.JcrResourceConstants;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+/**
+ * Testing that nested property container resources are available when that
+ * capability is enabled via configuration.
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class NestedAuthorizableResourcesIT extends BaseAuthorizableResourcesIT {
+
+ @Override
+ protected Option[] additionalOptions() {
+ return new Option[] {
+ newConfiguration("org.apache.sling.jackrabbit.usermanager.impl.resource.AuthorizableResourceProvider")
+ .put("resources.for.nested.properties", true)
+ .asOption()
+ };
+ }
+
+ /**
+ * Test resolving the nested properties of resources fails reasonably when the
+ * path doesn't exist
+ */
+ @Test
+ public void checkNotExistingNestedResources() throws LoginException, RepositoryException, IOException {
+ Map<String, Object> nestedProps = new HashMap<>();
+ nestedProps.put("key1", "value1");
+ nestedProps.put("private/key2", "value2");
+ user1 = createUser.createUser(adminSession, createUniqueName("user"), "testPwd", "testPwd",
+ nestedProps, new ArrayList<>());
+ assertNotNull("Expected user1 to not be null", user1);
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()));
+ assertTrue("Expected resource type of sling/user for: " + resource.getPath(),
+ resource.isResourceType("sling/user"));
+
+ @NotNull
+ Resource notExisting = resourceResolver.resolve(String.format("%s%s/notexisting", userManagerPaths.getUserPrefix(), user1.getID()));
+ assertNotNull(notExisting);
+ assertTrue(notExisting.isResourceType(Resource.RESOURCE_TYPE_NON_EXISTING));
+
+ @NotNull
+ Resource nestedNotExisting = resourceResolver.resolve(String.format("%s%s/private/notexisting", userManagerPaths.getUserPrefix(), user1.getID()));
+ assertNotNull(nestedNotExisting);
+ assertTrue(nestedNotExisting.isResourceType(Resource.RESOURCE_TYPE_NON_EXISTING));
+ }
+ }
+
+ /**
+ * Test resolving the nested properties of user resources
+ */
+ @Test
+ public void checkNestedUserPropertyResources() throws LoginException, RepositoryException, IOException {
+ Map<String, Object> nestedProps = new HashMap<>();
+ nestedProps.put("key1", "value1");
+ nestedProps.put("private/key2", "value2");
+ nestedProps.put("private/sub/key3", "value3");
+ user1 = createUser.createUser(adminSession, createUniqueName("user"), "testPwd", "testPwd",
+ nestedProps, new ArrayList<>());
+ assertNotNull("Expected user1 to not be null", user1);
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()));
+ assertTrue("Expected resource type of sling/user for: " + resource.getPath(),
+ resource.isResourceType("sling/user"));
+
+ @NotNull
+ ValueMap valueMap = resource.getValueMap();
+ assertEquals("Expected value1 for key1 property of: " + resource.getPath(),
+ "value1", valueMap.get("key1"));
+
+ @Nullable
+ Resource child = resource.getChild("private");
+ assertNotNull(child);
+ assertTrue("Expected resource type of sling/user/properties for: " + child.getPath(),
+ child.isResourceType("sling/user/properties"));
+ @NotNull
+ ValueMap childValueMap = child.getValueMap();
+ assertEquals("Expected value2 for key2 property of: " + child.getPath(),
+ "value2", childValueMap.get("key2"));
+
+ @Nullable
+ Resource grandchild = child.getChild("sub");
+ assertNotNull(grandchild);
+ assertTrue("Expected resource type of sling/user/properties for: " + grandchild.getPath(),
+ grandchild.isResourceType("sling/user/properties"));
+ @NotNull
+ ValueMap grandchildValueMap = grandchild.getValueMap();
+ assertEquals("Expected value3 for key3 property of: " + grandchild.getPath(),
+ "value3", grandchildValueMap.get("key3"));
+
+ //try access via iteration over the children
+ @NotNull
+ Iterable<Resource> children = resource.getChildren();
+ assertNotNull(children);
+ for (Resource child2 : children) {
+ assertNotNull(child2);
+ assertTrue("Expected resource type of sling/user/properties for: " + child2.getPath(),
+ child2.isResourceType("sling/user/properties"));
+ }
+ @NotNull
+ Iterable<Resource> grandchildren = grandchild.getChildren();
+ assertNotNull(grandchildren);
+ for (Resource child2 : grandchildren) {
+ assertNotNull(child2);
+ assertTrue("Expected resource type of sling/user/properties for: " + child2.getPath(),
+ child2.isResourceType("sling/user/properties"));
+ }
+ }
+ }
+
+ /**
+ * Test resolving the nested properties of group resources
+ */
+ @Test
+ public void checkNestedGroupPropertyResources() throws LoginException, RepositoryException, IOException {
+ Map<String, Object> nestedProps = new HashMap<>();
+ nestedProps.put("key1", "value1");
+ nestedProps.put("private/key2", "value2");
+ nestedProps.put("private/sub/key3", "value3");
+
+ group1 = createGroup.createGroup(adminSession, createUniqueName("group"),
+ nestedProps, new ArrayList<>());
+ assertNotNull("Expected group1 to not be null", group1);
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()));
+ assertTrue("Expected resource type of sling/group for: " + resource.getPath(),
+ resource.isResourceType("sling/group"));
+
+ @NotNull
+ ValueMap valueMap = resource.getValueMap();
+ assertEquals("Expected value1 for key1 property of: " + resource.getPath(),
+ "value1", valueMap.get("key1"));
+
+ @Nullable
+ Resource child = resource.getChild("private");
+ assertNotNull(child);
+ assertTrue("Expected resource type of sling/group/properties for: " + child.getPath(),
+ child.isResourceType("sling/group/properties"));
+ @NotNull
+ ValueMap childValueMap = child.getValueMap();
+ assertEquals("Expected value2 for key2 property of: " + child.getPath(),
+ "value2", childValueMap.get("key2"));
+
+ @Nullable
+ Resource grandchild = child.getChild("sub");
+ assertNotNull(grandchild);
+ assertTrue("Expected resource type of sling/group/properties for: " + grandchild.getPath(),
+ grandchild.isResourceType("sling/group/properties"));
+ @NotNull
+ ValueMap grandchildValueMap = grandchild.getValueMap();
+ assertEquals("Expected value3 for key3 property of: " + grandchild.getPath(),
+ "value3", grandchildValueMap.get("key3"));
+
+ //try access via iteration over the children
+ @NotNull
+ Iterable<Resource> children = resource.getChildren();
+ assertNotNull(children);
+ for (Resource child2 : children) {
+ assertNotNull(child2);
+ assertTrue("Expected resource type of sling/group/properties for: " + child2.getPath(),
+ child2.isResourceType("sling/group/properties"));
+ }
+ @NotNull
+ Iterable<Resource> grandchildren = grandchild.getChildren();
+ assertNotNull(grandchildren);
+ for (Resource child2 : grandchildren) {
+ assertNotNull(child2);
+ assertTrue("Expected resource type of sling/group/properties for: " + child2.getPath(),
+ child2.isResourceType("sling/group/properties"));
+ }
+ }
+ }
+
+ @Test
+ public void adaptNestedResourceToMap() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ Map<?, ?> groupMap = groupResource.adaptTo(Map.class);
+ assertNotNull(groupMap);
+ assertEquals("value2", groupMap.get("key2"));
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ Map<?, ?> userMap = userResource.adaptTo(Map.class);
+ assertNotNull(userMap);
+ assertEquals("value2", userMap.get("key2"));
+ }
+ }
+
+ @Test
+ public void adaptNestedResourceToValueMap() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ ValueMap groupMap = groupResource.adaptTo(ValueMap.class);
+ assertNotNull(groupMap);
+ assertEquals("NestedAuthorizableValueMap", groupMap.getClass().getSimpleName());
+ assertEquals("value2", groupMap.get("key2"));
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ ValueMap userMap = userResource.adaptTo(ValueMap.class);
+ assertNotNull(userMap);
+ assertEquals("NestedAuthorizableValueMap", userMap.getClass().getSimpleName());
+ assertEquals("value2", userMap.get("key2"));
+ }
+ }
+
+ @Test
+ public void adaptNestedResourceToAuthorizable() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ Authorizable groupAuthorizable = groupResource.adaptTo(Authorizable.class);
+ assertNotNull(groupAuthorizable);
+ assertEquals(group1.getID(), groupAuthorizable.getID());
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ Authorizable userAuthorizable = userResource.adaptTo(Authorizable.class);
+ assertNotNull(userAuthorizable);
+ assertEquals(user1.getID(), userAuthorizable.getID());
+ }
+ }
+
+ @Test
+ public void adaptNestedResourceToUserOrGroup() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ Group group = groupResource.adaptTo(Group.class);
+ assertNotNull(group);
+ assertEquals(group1.getID(), group.getID());
+ assertNull(groupResource.adaptTo(User.class));
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ User user = userResource.adaptTo(User.class);
+ assertNotNull(user);
+ assertEquals(user1.getID(), user.getID());
+ assertNull(userResource.adaptTo(Group.class));
+ }
+ }
+
+ /**
+ * For code coverage, test some adaption that falls through to the super class impl
+ */
+ @Test
+ public void adaptNestedResourceToSomethingElse() throws LoginException, RepositoryException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @Nullable
+ NestedAuthorizableResourcesIT groupObj = groupResource.adaptTo(NestedAuthorizableResourcesIT.class);
+ assertNull(groupObj);
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getUserPrefix(), user1.getID()));
+ @Nullable
+ NestedAuthorizableResourcesIT userObj = userResource.adaptTo(NestedAuthorizableResourcesIT.class);
+ assertNull(userObj);
+ }
+ }
+
+ /**
+ * Test iteration of the usermanager nested resource children
+ */
+ @Test
+ public void listNestedChildren() throws LoginException, RepositoryException, IOException {
+ createResourcesForAdaptTo();
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource groupResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getGroupPrefix(), group1.getID()));
+ @NotNull
+ Iterator<Resource> children = groupResource.listChildren();
+ assertNotNull(children);
+ assertTrue(children.hasNext());
+ for (Iterator<Resource> iterator = children; iterator.hasNext();) {
+ Resource child = (Resource) iterator.next();
+ assertTrue(child.isResourceType("sling/group/properties"));
+ }
+
+ Resource userResource = resourceResolver.resolve(String.format("%s%s/private", userManagerPaths.getUserPrefix(), user1.getID()));
+ @NotNull
+ Iterator<Resource> children2 = userResource.listChildren();
+ assertNotNull(children2);
+ assertTrue(children2.hasNext());
+ for (Iterator<Resource> iterator = children2; iterator.hasNext();) {
+ Resource child = (Resource) iterator.next();
+ assertTrue(child.isResourceType("sling/user/properties"));
+ }
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/NestedAuthorizableValueMapIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/NestedAuthorizableValueMapIT.java
new file mode 100644
index 0000000..d889185
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/NestedAuthorizableValueMapIT.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jackrabbit.usermanager.it.resource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+
+import java.util.Collections;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.api.JcrResourceConstants;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+/**
+ * Basic test of NestedAuthorizableValueMap
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class NestedAuthorizableValueMapIT extends BaseAuthorizableValueMapIT {
+
+ @Override
+ protected Option[] additionalOptions() {
+ return new Option[] {
+ newConfiguration("org.apache.sling.jackrabbit.usermanager.impl.resource.AuthorizableResourceProvider")
+ .put("resources.for.nested.properties", true)
+ .asOption()
+ };
+ }
+
+ @Override
+ protected Map<String, Object> createAuthorizableProps() throws LoginException {
+ return createAuthorizableProps("nested/");
+ }
+
+ @Override
+ protected ValueMap getValueMap(Authorizable a) throws LoginException, RepositoryException {
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource;
+ if (a.isGroup()) {
+ resource = resourceResolver.resolve(String.format("%s%s/nested", userManagerPaths.getGroupPrefix(), a.getID()));
+ } else {
+ resource = resourceResolver.resolve(String.format("%s%s/nested", userManagerPaths.getUserPrefix(), a.getID()));
+ }
+ assertNotNull(resource);
+ ValueMap vm = resource.adaptTo(ValueMap.class);
+ assertNotNull(vm);
+ return vm;
+ }
+ }
+
+ @Test
+ @Override
+ public void testSize() throws LoginException, RepositoryException {
+ ValueMap vm = getValueMap(user1);
+ int size = vm.size();
+ assertEquals(27, size);
+
+ ValueMap vm2 = getValueMap(group1);
+ int size2 = vm2.size();
+ assertEquals(27, size2);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/NoNestedAuthorizableResourcesIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/NoNestedAuthorizableResourcesIT.java
new file mode 100644
index 0000000..a1f443b
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/resource/NoNestedAuthorizableResourcesIT.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jackrabbit.usermanager.it.resource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.api.JcrResourceConstants;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+/**
+ * Testing that no nested property container resources are available when that
+ * capability is disabled via configuration.
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class NoNestedAuthorizableResourcesIT extends BaseAuthorizableResourcesIT {
+
+ @Override
+ protected Option[] additionalOptions() {
+ return new Option[] {
+ newConfiguration("org.apache.sling.jackrabbit.usermanager.impl.resource.AuthorizableResourceProvider")
+ .put("resources.for.nested.properties", false)
+ .asOption()
+ };
+ }
+
+ /**
+ * Test resolving that there are no nested properties of user resources
+ */
+ @Test
+ public void checkNoNestedUserPropertyResources() throws LoginException, RepositoryException, IOException {
+ Map<String, Object> nestedProps = new HashMap<>();
+ nestedProps.put("key1", "value1");
+ nestedProps.put("private/key2", "value2");
+ nestedProps.put("private/sub/key3", "value3");
+ user1 = createUser.createUser(adminSession, createUniqueName("user"), "testPwd", "testPwd",
+ nestedProps, new ArrayList<>());
+ assertNotNull("Expected user1 to not be null", user1);
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getUserPrefix(), user1.getID()));
+ assertTrue("Expected resource type of sling/user for: " + resource.getPath(),
+ resource.isResourceType("sling/user"));
+
+ @NotNull
+ ValueMap valueMap = resource.getValueMap();
+ assertEquals("Expected value1 for key1 property of: " + resource.getPath(),
+ "value1", valueMap.get("key1"));
+
+ @Nullable
+ Resource child = resource.getChild("private");
+ assertNull(child);
+
+ @Nullable
+ Resource grandchild = resource.getChild("private/sub");
+ assertNull(grandchild);
+ }
+ }
+
+ /**
+ * Test resolving that there are no nested properties of group resources
+ */
+ @Test
+ public void checkNoNestedGroupPropertyResources() throws LoginException, RepositoryException, IOException {
+ Map<String, Object> nestedProps = new HashMap<>();
+ nestedProps.put("key1", "value1");
+ nestedProps.put("private/key2", "value2");
+ nestedProps.put("private/sub/key3", "value3");
+
+ group1 = createGroup.createGroup(adminSession, createUniqueName("group"),
+ nestedProps, new ArrayList<>());
+ assertNotNull("Expected group1 to not be null", group1);
+
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, adminSession))) {
+ Resource resource = resourceResolver.resolve(String.format("%s%s", userManagerPaths.getGroupPrefix(), group1.getID()));
+ assertTrue("Expected resource type of sling/group for: " + resource.getPath(),
+ resource.isResourceType("sling/group"));
+
+ @NotNull
+ ValueMap valueMap = resource.getValueMap();
+ assertEquals("Expected value1 for key1 property of: " + resource.getPath(),
+ "value1", valueMap.get("key1"));
+
+ @Nullable
+ Resource child = resource.getChild("private");
+ assertNull(child);
+
+ @Nullable
+ Resource grandchild = resource.getChild("private/sub");
+ assertNull(grandchild);
+ }
+ }
+
+}