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 31a8a30 SLING-9807 AuthorizablePrivilegesInfo is checking for too may
privileges for some of the operations
31a8a30 is described below
commit 31a8a306f834945d9f079b5671a01b6581b8526a
Author: Eric Norman <[email protected]>
AuthorDate: Sat Oct 10 12:41:18 2020 -0700
SLING-9807 AuthorizablePrivilegesInfo is checking for too may privileges
for some of the operations
---
pom.xml | 104 +++
.../usermanager/AuthorizablePrivilegesInfo.java | 25 +
.../impl/AuthorizablePrivilegesInfoImpl.java | 251 +++++---
.../sling/jackrabbit/usermanager/package-info.java | 2 +-
.../it/AuthorizablePrivilegesInfoIT.java | 711 +++++++++++++++++++++
.../usermanager/it/UserManagerTestSupport.java | 76 +++
6 files changed, 1063 insertions(+), 106 deletions(-)
diff --git a/pom.xml b/pom.xml
index 3d87292..6b2c905 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,6 +37,7 @@
<properties>
<project.build.outputTimestamp>2020-07-27T15:10:15Z</project.build.outputTimestamp>
+ <org.ops4j.pax.exam.version>4.13.3</org.ops4j.pax.exam.version>
</properties>
<scm>
@@ -66,6 +67,32 @@
<groupId>biz.aQute.bnd</groupId>
<artifactId>bnd-maven-plugin</artifactId>
</plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <redirectTestOutputToFile>true</redirectTestOutputToFile>
+ <systemProperties>
+ <property>
+ <name>bundle.filename</name>
+
<value>${basedir}/target/${project.build.finalName}.jar</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ </plugin>
</plugins>
</build>
@@ -147,5 +174,82 @@
<version>1.8.8</version>
<optional>true</optional>
</dependency>
+
+
+ <!-- Sling Testing PaxExam -->
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.paxexam</artifactId>
+ <version>3.1.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- an OSGi framework -->
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ <version>6.0.3</version>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- JUnit -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- Pax Exam -->
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-cm</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-forked</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-link-mvn</artifactId>
+ <version>${org.ops4j.pax.exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.cmpn</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+
<artifactId>org.apache.sling.jcr.jackrabbit.accessmanager</artifactId>
+ <version>3.0.6</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git
a/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
b/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
index c500dd8..af86136 100644
---
a/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
+++
b/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
@@ -21,6 +21,16 @@ import javax.jcr.Session;
public interface AuthorizablePrivilegesInfo {
/**
+ * An enumeration of the possible types of property changes
+ */
+ public enum PropertyUpdateTypes {
+ addProperty,
+ addNestedProperty,
+ alterProperty,
+ removeProperty
+ }
+
+ /**
* Checks whether the current user has been granted privileges
* to add a new user.
*
@@ -41,6 +51,7 @@ public interface AuthorizablePrivilegesInfo {
/**
* Checks whether the current user has been granted privileges
* to update the properties of the specified user or group.
+ * Equivalent of: #canUpdateProperties(Session, String,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty,
PropertyUpdateTypes.alterProperty, PropertyUpdateTypes.removeProperty)
*
* @param jcrSession the JCR session of the current user
* @param principalId the user or group id to check
@@ -51,6 +62,20 @@ public interface AuthorizablePrivilegesInfo {
/**
* Checks whether the current user has been granted privileges
+ * to update the properties of the specified user or group.
+ *
+ * @param jcrSession the JCR session of the current user
+ * @param principalId the user or group id to check
+ * @param propertyUpdateTypes specify the types of property updates that
may be supplied. See: {@link PropertyUpdateTypes}
+ * @return true if the current user has the privileges, false otherwise
+ */
+ default boolean canUpdateProperties(Session jcrSession,
+ String principalId, PropertyUpdateTypes ... propertyUpdateTypes) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Checks whether the current user has been granted privileges
* to remove the specified user or group.
*
* @param jcrSession the JCR session of the current user
diff --git
a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/AuthorizablePrivilegesInfoImpl.java
b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/AuthorizablePrivilegesInfoImpl.java
index c82208a..e7da94c 100644
---
a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/AuthorizablePrivilegesInfoImpl.java
+++
b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/AuthorizablePrivilegesInfoImpl.java
@@ -19,7 +19,9 @@ package org.apache.sling.jackrabbit.usermanager.impl;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
@@ -59,8 +61,8 @@ import org.slf4j.LoggerFactory;
*/
@Component(service=AuthorizablePrivilegesInfo.class,
property={
- AuthorizablePrivilegesInfoImpl.PAR_USER_ADMIN_GROUP_NAME + "="
+ AuthorizablePrivilegesInfoImpl.DEFAULT_USER_ADMIN_GROUP_NAME,
- AuthorizablePrivilegesInfoImpl.PAR_GROUP_ADMIN_GROUP_NAME + "="
+ AuthorizablePrivilegesInfoImpl.DEFAULT_GROUP_ADMIN_GROUP_NAME
+ AuthorizablePrivilegesInfoImpl.PAR_USER_ADMIN_GROUP_NAME + "=" +
AuthorizablePrivilegesInfoImpl.DEFAULT_USER_ADMIN_GROUP_NAME,
+ AuthorizablePrivilegesInfoImpl.PAR_GROUP_ADMIN_GROUP_NAME + "=" +
AuthorizablePrivilegesInfoImpl.DEFAULT_GROUP_ADMIN_GROUP_NAME
})
public class AuthorizablePrivilegesInfoImpl implements
AuthorizablePrivilegesInfo {
@@ -99,186 +101,225 @@ public class AuthorizablePrivilegesInfoImpl implements
AuthorizablePrivilegesInf
@Reference(cardinality=ReferenceCardinality.OPTIONAL, policy =
ReferencePolicy.DYNAMIC)
private void bindUserConfiguration(UserConfiguration userConfig,
Map<String, Object> properties) {
- usersPath = (String)properties.get(UserConstants.PARAM_USER_PATH);
- groupsPath = (String)properties.get(UserConstants.PARAM_GROUP_PATH);
+ usersPath = (String)properties.get(UserConstants.PARAM_USER_PATH);
+ groupsPath = (String)properties.get(UserConstants.PARAM_GROUP_PATH);
}
@SuppressWarnings("unused")
- private void unbindUserConfiguration(UserConfiguration userConfig,
Map<String, Object> properties) {
- usersPath = null;
- groupsPath = null;
+ private void unbindUserConfiguration(UserConfiguration userConfig,
Map<String, Object> properties) {
+ usersPath = null;
+ groupsPath = null;
}
@Reference(cardinality=ReferenceCardinality.OPTIONAL, policy =
ReferencePolicy.DYNAMIC)
private void bindCreateUser(CreateUser createUser, Map<String, Object>
properties) {
- selfRegistrationEnabled =
Boolean.TRUE.equals(properties.get("self.registration.enabled"));
+ selfRegistrationEnabled =
Boolean.TRUE.equals(properties.get("self.registration.enabled"));
}
@SuppressWarnings("unused")
- private void unbindCreateUser(CreateUser createUser, Map<String,
Object> properties) {
- selfRegistrationEnabled = false;
+ private void unbindCreateUser(CreateUser createUser, Map<String, Object>
properties) {
+ selfRegistrationEnabled = false;
}
/* (non-Javadoc)
* @see
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canAddGroup(javax.jcr.Session)
*/
public boolean canAddGroup(Session jcrSession) {
+ boolean hasRights = false;
try {
UserManager userManager =
AccessControlUtil.getUserManager(jcrSession);
Authorizable currentUser =
userManager.getAuthorizable(jcrSession.getUserID());
- if (currentUser != null) {
- if (((User)currentUser).isAdmin()) {
- return true; //admin user has full control
+ if (currentUser instanceof User && ((User)currentUser).isAdmin()) {
+ hasRights = true; //admin user has full control
+ } else {
+ if (groupsPath != null) {
+ //check if the non-admin user has sufficient rights on the
home folder
+ AccessControlManager acm =
jcrSession.getAccessControlManager();
+ hasRights = acm.hasPrivileges(groupsPath, new Privilege[] {
+
acm.privilegeFromName(Privilege.JCR_READ),
+
acm.privilegeFromName(Privilege.JCR_READ_ACCESS_CONTROL),
+
acm.privilegeFromName(Privilege.JCR_MODIFY_ACCESS_CONTROL),
+
acm.privilegeFromName(PrivilegeConstants.REP_WRITE),
+
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
+ });
}
}
-
- if (groupsPath != null) {
- //check if the non-admin user has sufficient rights on the
home folder
- AccessControlManager acm =
jcrSession.getAccessControlManager();
- boolean hasRights = acm.hasPrivileges(groupsPath, new
Privilege[] {
-
acm.privilegeFromName(Privilege.JCR_READ),
-
acm.privilegeFromName(Privilege.JCR_READ_ACCESS_CONTROL),
-
acm.privilegeFromName(Privilege.JCR_MODIFY_ACCESS_CONTROL),
-
acm.privilegeFromName(PrivilegeConstants.REP_WRITE),
-
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
- });
- return hasRights;
- }
} catch (RepositoryException e) {
log.warn("Failed to determine if {} can add a new group",
jcrSession.getUserID());
}
- return false;
+ return hasRights;
}
/* (non-Javadoc)
* @see
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canAddUser(javax.jcr.Session)
*/
public boolean canAddUser(Session jcrSession) {
+ boolean hasRights = false;
try {
//if self-registration is enabled, then anyone can create a user
- if (selfRegistrationEnabled) {
- return true;
- }
-
- UserManager userManager =
AccessControlUtil.getUserManager(jcrSession);
- Authorizable currentUser =
userManager.getAuthorizable(jcrSession.getUserID());
- if (currentUser != null) {
- if (((User)currentUser).isAdmin()) {
- return true; //admin user has full control
+ if (selfRegistrationEnabled) {
+ hasRights = true;
+ } else {
+ UserManager userManager =
AccessControlUtil.getUserManager(jcrSession);
+ Authorizable currentUser =
userManager.getAuthorizable(jcrSession.getUserID());
+ if (currentUser instanceof User &&
((User)currentUser).isAdmin()) {
+ hasRights = true; //admin user has full control
+ } else {
+ if (usersPath != null) {
+ //check if the non-admin user has sufficient rights on
the home folder
+ AccessControlManager acm =
jcrSession.getAccessControlManager();
+ hasRights = acm.hasPrivileges(usersPath, new
Privilege[] {
+
acm.privilegeFromName(Privilege.JCR_READ),
+
acm.privilegeFromName(Privilege.JCR_READ_ACCESS_CONTROL),
+
acm.privilegeFromName(Privilege.JCR_MODIFY_ACCESS_CONTROL),
+
acm.privilegeFromName(PrivilegeConstants.REP_WRITE),
+
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
+ });
+ }
}
}
-
- if (usersPath != null) {
- //check if the non-admin user has sufficient rights on the
home folder
- AccessControlManager acm =
jcrSession.getAccessControlManager();
- boolean hasRights = acm.hasPrivileges(usersPath, new
Privilege[] {
-
acm.privilegeFromName(Privilege.JCR_READ),
-
acm.privilegeFromName(Privilege.JCR_READ_ACCESS_CONTROL),
-
acm.privilegeFromName(Privilege.JCR_MODIFY_ACCESS_CONTROL),
-
acm.privilegeFromName(PrivilegeConstants.REP_WRITE),
-
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
- });
- return hasRights;
- }
} catch (RepositoryException e) {
log.warn("Failed to determine if {} can add a new user",
jcrSession.getUserID());
}
- return false;
+ return hasRights;
}
/* (non-Javadoc)
* @see
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canRemove(javax.jcr.Session,
java.lang.String)
*/
public boolean canRemove(Session jcrSession, String principalId) {
+ boolean hasRights = false;
try {
UserManager userManager =
AccessControlUtil.getUserManager(jcrSession);
Authorizable currentUser =
userManager.getAuthorizable(jcrSession.getUserID());
- if (((User)currentUser).isAdmin()) {
- return true; //admin user has full control
- }
-
- if (usersPath != null) {
- //check if the non-admin user has sufficient rights on the
home folder
- AccessControlManager acm =
jcrSession.getAccessControlManager();
- boolean hasRights = acm.hasPrivileges("/home/users", new
Privilege[] {
-
acm.privilegeFromName(Privilege.JCR_READ),
-
acm.privilegeFromName(Privilege.JCR_READ_ACCESS_CONTROL),
-
acm.privilegeFromName(Privilege.JCR_MODIFY_ACCESS_CONTROL),
-
acm.privilegeFromName(PrivilegeConstants.REP_WRITE),
-
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
- });
- return hasRights;
+ if (currentUser instanceof User && ((User)currentUser).isAdmin()) {
+ hasRights = true; //admin user has full control
+ } else {
+ Authorizable authorizable =
userManager.getAuthorizable(principalId);
+ if (authorizable == null) {
+ log.debug("Failed to find authorizable: {}", principalId);
+ } else {
+ String path = authorizable.getPath();
+ if (path != null) {
+ //check if the non-admin user has sufficient rights on
the home folder
+ AccessControlManager acm =
jcrSession.getAccessControlManager();
+ hasRights = acm.hasPrivileges(path, new Privilege[] {
+
acm.privilegeFromName(Privilege.JCR_READ),
+
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
+ });
+ }
+ }
}
} catch (RepositoryException e) {
log.warn("Failed to determine if {} can remove authorizable {}",
jcrSession.getUserID(), principalId);
}
- return false;
+ return hasRights;
}
/* (non-Javadoc)
* @see
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canUpdateGroupMembers(javax.jcr.Session,
java.lang.String)
*/
public boolean canUpdateGroupMembers(Session jcrSession, String groupId) {
+ boolean hasRights = false;
try {
UserManager userManager =
AccessControlUtil.getUserManager(jcrSession);
Authorizable currentUser =
userManager.getAuthorizable(jcrSession.getUserID());
- if (((User)currentUser).isAdmin()) {
- return true; //admin user has full control
- }
-
- if (groupsPath != null) {
- //check if the non-admin user has sufficient rights on the
home folder
- AccessControlManager acm =
jcrSession.getAccessControlManager();
- boolean hasRights = acm.hasPrivileges(groupsPath, new
Privilege[] {
-
acm.privilegeFromName(Privilege.JCR_READ),
-
acm.privilegeFromName(Privilege.JCR_READ_ACCESS_CONTROL),
-
acm.privilegeFromName(Privilege.JCR_MODIFY_ACCESS_CONTROL),
-
acm.privilegeFromName(PrivilegeConstants.REP_WRITE),
-
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
- });
- return hasRights;
+ if (currentUser instanceof User && ((User)currentUser).isAdmin()) {
+ hasRights = true; //admin user has full control
+ } else {
+ Authorizable authorizable =
userManager.getAuthorizable(groupId);
+ if (authorizable == null) {
+ log.debug("Failed to find group: {}", groupId);
+ } else {
+ String path = authorizable.getPath();
+ if (path != null) {
+ //check if the non-admin user has sufficient rights on
the home folder
+ AccessControlManager acm =
jcrSession.getAccessControlManager();
+ hasRights = acm.hasPrivileges(path, new Privilege[] {
+
acm.privilegeFromName(Privilege.JCR_READ),
+
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
+ });
+ }
+ }
}
} catch (RepositoryException e) {
log.warn("Failed to determine if {} can remove authorizable {}",
jcrSession.getUserID(), groupId);
}
- return false;
+ return hasRights;
}
/* (non-Javadoc)
* @see
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canUpdateProperties(javax.jcr.Session,
java.lang.String)
*/
public boolean canUpdateProperties(Session jcrSession, String principalId)
{
+ return canUpdateProperties(jcrSession, principalId,
+ PropertyUpdateTypes.addProperty,
PropertyUpdateTypes.addNestedProperty,
+ PropertyUpdateTypes.alterProperty,
PropertyUpdateTypes.removeProperty);
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canUpdateProperties(javax.jcr.Session,
java.lang.String,
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo.PropertyUpdateTypes[])
+ */
+ @Override
+ public boolean canUpdateProperties(Session jcrSession, String principalId,
+ PropertyUpdateTypes... propertyUpdateTypes) {
+ boolean hasRights = false;
try {
if (jcrSession.getUserID().equals(principalId)) {
//user is allowed to update it's own properties
- return true;
- }
-
- UserManager userManager =
AccessControlUtil.getUserManager(jcrSession);
- Authorizable currentUser =
userManager.getAuthorizable(jcrSession.getUserID());
+ hasRights = true;
+ } else {
+ UserManager userManager =
AccessControlUtil.getUserManager(jcrSession);
+ Authorizable currentUser =
userManager.getAuthorizable(jcrSession.getUserID());
- if (((User)currentUser).isAdmin()) {
- return true; //admin user has full control
- }
-
- String path = currentUser.isGroup() ? groupsPath : usersPath;
- if (path != null) {
- //check if the non-admin user has sufficient rights on the
home folder
- AccessControlManager acm =
jcrSession.getAccessControlManager();
- boolean hasRights = acm.hasPrivileges(path, new Privilege[] {
-
acm.privilegeFromName(Privilege.JCR_READ),
-
acm.privilegeFromName(Privilege.JCR_READ_ACCESS_CONTROL),
-
acm.privilegeFromName(Privilege.JCR_MODIFY_ACCESS_CONTROL),
-
acm.privilegeFromName(PrivilegeConstants.REP_WRITE),
-
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
- });
- return hasRights;
+ if (currentUser instanceof User) {
+ if (((User)currentUser).isAdmin()) {
+ return true; //admin user has full control
+ }
+ }
+
+ Authorizable authorizable =
userManager.getAuthorizable(principalId);
+ if (authorizable == null) {
+ log.debug("Failed to find authorizable: {}", principalId);
+ } else {
+ String path = authorizable.getPath();
+ if (path != null) {
+ //check if the non-admin user has sufficient rights on
the home folder
+ AccessControlManager acm =
jcrSession.getAccessControlManager();
+ Set<Privilege> requiredPrivileges = new HashSet<>();
+
requiredPrivileges.add(acm.privilegeFromName(Privilege.JCR_READ));
+ if (propertyUpdateTypes != null) {
+ for (PropertyUpdateTypes updateType :
propertyUpdateTypes) {
+ switch (updateType) {
+ case addNestedProperty:
+
requiredPrivileges.add(acm.privilegeFromName(PrivilegeConstants.REP_ADD_PROPERTIES));
+
requiredPrivileges.add(acm.privilegeFromName(Privilege.JCR_ADD_CHILD_NODES));
+ break;
+ case addProperty:
+
requiredPrivileges.add(acm.privilegeFromName(PrivilegeConstants.REP_ADD_PROPERTIES));
+ break;
+ case alterProperty:
+
requiredPrivileges.add(acm.privilegeFromName(PrivilegeConstants.REP_ALTER_PROPERTIES));
+ break;
+ case removeProperty:
+
requiredPrivileges.add(acm.privilegeFromName(PrivilegeConstants.REP_REMOVE_PROPERTIES));
+ break;
+ default:
+ log.warn("Unexpected property update type:
{}", updateType);
+ break;
+ }
+ }
+ }
+
+ hasRights = acm.hasPrivileges(path,
requiredPrivileges.toArray(new Privilege[requiredPrivileges.size()]));
+ }
+ }
}
} catch (RepositoryException e) {
- log.warn("Failed to determine if {} can remove authorizable {}",
jcrSession.getUserID(), principalId);
+ log.warn("Failed to determine if {} can update properties of
authorizable {}", jcrSession.getUserID(), principalId);
}
- return false;
+ return hasRights;
}
diff --git
a/src/main/java/org/apache/sling/jackrabbit/usermanager/package-info.java
b/src/main/java/org/apache/sling/jackrabbit/usermanager/package-info.java
index 02521b0..c236241 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/package-info.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/package-info.java
@@ -17,7 +17,7 @@
* under the License.
*/
[email protected]("2.2.1")
[email protected]("2.3.0")
package org.apache.sling.jackrabbit.usermanager;
diff --git
a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/AuthorizablePrivilegesInfoIT.java
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/AuthorizablePrivilegesInfoIT.java
new file mode 100644
index 0000000..7dc8196
--- /dev/null
+++
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/AuthorizablePrivilegesInfoIT.java
@@ -0,0 +1,711 @@
+/*
+ * 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;
+
+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 static org.junit.Assert.fail;
+import static org.ops4j.pax.exam.CoreOptions.options;
+
+import java.util.ArrayList;
+import java.util.Collections;
+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 javax.jcr.security.Privilege;
+
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo;
+import
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo.PropertyUpdateTypes;
+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.UpdateGroup;
+import org.apache.sling.jackrabbit.usermanager.UpdateUser;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.base.util.AccessControlUtil;
+import org.apache.sling.jcr.jackrabbit.accessmanager.ModifyAce;
+import org.apache.sling.servlets.post.Modification;
+import org.junit.After;
+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.Configuration;
+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;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Basic test of AuthorizablePrivilegesInfo component
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class AuthorizablePrivilegesInfoIT extends UserManagerTestSupport {
+ private static AtomicLong counter = new AtomicLong(0);
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ @Inject
+ protected BundleContext bundleContext;
+
+ @Inject
+ protected SlingRepository repository;
+
+ @Inject
+ private AuthorizablePrivilegesInfo privilegesInfo;
+
+ @Inject
+ private UserConfiguration userConfig;
+
+ @Inject
+ private CreateUser createUser;
+
+ @Inject
+ private CreateGroup createGroup;
+
+ @Inject
+ private UpdateUser updateUser;
+
+ @Inject
+ private UpdateGroup updateGroup;
+
+ @Inject
+ private DeleteUser deleteUser;
+
+ @Inject
+ private DeleteGroup deleteGroup;
+
+ @Inject
+ private ModifyAce modifyAce;
+
+ @Rule
+ public TestName testName = new TestName();
+
+ protected Session adminSession;
+ protected User user1;
+ protected Session user1Session;
+
+ @Configuration
+ public Option[] configuration() {
+ return options(
+ baseConfiguration()
+ );
+ }
+
+ @Before
+ public void setup() throws Exception {
+ adminSession = repository.login(new SimpleCredentials("admin",
"admin".toCharArray()));
+ assertNotNull("Expected adminSession to not be null",
adminSession);
+
+ user1 = createUser.createUser(adminSession,
createUniqueName("user"), "testPwd", "testPwd",
+ Collections.emptyMap(), new
ArrayList<Modification>());
+ assertNotNull("Expected user1 to not be null", user1);
+
+ user1Session = repository.login(new
SimpleCredentials(user1.getID(), "testPwd".toCharArray()));
+ assertNotNull("Expected user1Session to not be null",
user1Session);
+ }
+
+ @After
+ public void teardown() {
+ if (user1 != null) {
+ try {
+ adminSession.refresh(false);
+ deleteUser.deleteUser(adminSession,
user1.getID(), new ArrayList<>());
+ } catch (RepositoryException e) {
+ logger.warn("Failed to delete user: " +
e.getMessage(), e);
+ }
+ }
+ user1Session.logout();
+ adminSession.logout();
+ }
+
+ protected String createUniqueName(String prefix) {
+ return String.format("%s_%s%d", prefix,
testName.getMethodName(), counter.incrementAndGet());
+ }
+
+ /**
+ * Checks whether the current user has been granted privileges
+ * to add a new user. Equivalent of: #canAddUser(Session,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty)
+ */
+ @Test
+ public void canAddUser() throws Exception {
+ assertNotNull("Expected privilegesInfo to not be null",
privilegesInfo);
+
+ User user2 = null;
+ try {
+ // initially user can't do the operations
+ assertFalse("Should not be allowed to add user",
+
privilegesInfo.canAddUser(user1Session));
+
+ String usersPath =
userConfig.getParameters().getConfigValue("usersPath", (String)null);
+ assertNotNull("Users Path should not be null",
usersPath);
+ assertTrue("Users Path should exist",
+ adminSession.itemExists(usersPath));
+
+ // grant user1 rights
+ Map<String, String> privileges = new HashMap<>();
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_READ), "granted");
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_READ_ACCESS_CONTROL), "granted");
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_MODIFY_ACCESS_CONTROL), "granted");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_WRITE), "granted");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+ modifyAce.modifyAce(adminSession, usersPath,
user1.getID(),
+ privileges,
+ "first");
+ assertTrue("Should be allowed to add user",
+
privilegesInfo.canAddUser(user1Session));
+
+ // verify that the user can actually add the user
+ try {
+ Map<String, String> propMap = new HashMap<>();
+ propMap.put("prop1", "value1");
+ propMap.put("nested/prop2", "value2");
+ user2 = createUser.createUser(user1Session,
createUniqueName("user"), "testPwd", "testPwd",
+ propMap, new
ArrayList<Modification>());
+ assertNotNull("Expected user2 to not be null",
user2);
+ } catch (RepositoryException e) {
+ logger.error("Did not expect
RepositoryException when adding user: " + e.getMessage(), e);
+ fail("Did not expect RepositoryException when
adding user: " + e.getMessage());
+ }
+ } finally {
+ if (user2 != null) {
+ deleteUser.deleteUser(adminSession,
user2.getID(), new ArrayList<>());
+ }
+ }
+ }
+
+ /**
+ * Checks whether the current user has been granted privileges
+ * to add a new group. Equivalent of: #canAddGroup(Session,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty)
+ */
+ @Test
+ public void canAddGroup() throws Exception {
+ assertNotNull("Expected privilegesInfo to not be null",
privilegesInfo);
+
+ workaroundMissingGroupsPath();
+
+ Group group1 = null;
+ try {
+ // initially user can't do the operations
+ assertFalse("Should not be allowed to add group",
+
privilegesInfo.canAddGroup(user1Session));
+
+ String groupsPath =
userConfig.getParameters().getConfigValue("groupsPath", (String)null);
+ assertNotNull("Groups Path should not be null",
groupsPath);
+ assertTrue("Groups Path should exist",
+ adminSession.itemExists(groupsPath));
+
+ // grant user1 rights
+ Map<String, String> privileges = new HashMap<>();
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_READ), "granted");
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_READ_ACCESS_CONTROL), "granted");
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_MODIFY_ACCESS_CONTROL), "granted");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_WRITE), "granted");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+ modifyAce.modifyAce(adminSession, groupsPath,
user1.getID(),
+ privileges,
+ "first");
+ assertTrue("Should be allowed to add group",
+
privilegesInfo.canAddGroup(user1Session));
+
+ // verify that the user can actually add the user
+ try {
+ Map<String, String> propMap = new HashMap<>();
+ propMap.put("prop1", "value1");
+ propMap.put("nested/prop2", "value2");
+ group1 = createGroup.createGroup(user1Session,
createUniqueName("group"),
+ propMap, new
ArrayList<Modification>());
+ assertNotNull("Expected group1 to not be null",
group1);
+ } catch (RepositoryException e) {
+ logger.error("Did not expect
RepositoryException when adding group: " + e.getMessage(), e);
+ fail("Did not expect RepositoryException when
adding group: " + e.getMessage());
+ }
+ } finally {
+ if (group1 != null) {
+ deleteGroup.deleteGroup(user1Session,
group1.getID(), new ArrayList<>());
+ }
+ }
+ }
+
+ /**
+ * The oak groupsPath appears to be missing until the first group is
created.
+ * So create a group as the admin user to get it bootstrapped. This
makes it
+ * possible for non-admin users to create groups without requiring
extra access
+ * rights to the intermediate parents of the groupsPath folder.
+ */
+ protected void workaroundMissingGroupsPath() throws RepositoryException
{
+ String groupsPath =
userConfig.getParameters().getConfigValue("groupsPath", (String)null);
+ assertNotNull("Groups Path should not be null", groupsPath);
+ if (!adminSession.itemExists(groupsPath)) {
+ // create a group and the remove it
+ Group tempGroup = createGroup.createGroup(adminSession,
createUniqueName("group"),
+ Collections.emptyMap(), new
ArrayList<Modification>());
+ deleteGroup.deleteGroup(adminSession,
tempGroup.getID(), new ArrayList<>());
+ }
+ assertTrue("Groups Path should exist",
+ adminSession.itemExists(groupsPath));
+ }
+
+ /**
+ * Checks whether the current user has been granted privileges
+ * to update the properties of the specified user or group.
+ */
+ @Test
+ public void canUpdateProperties() throws Exception {
+ assertNotNull("Expected privilegesInfo to not be null",
privilegesInfo);
+
+ workaroundMissingGroupsPath();
+
+ User user2 = null;
+ Group group1 = null;
+
+ try {
+ // create a couple of test users
+ user2 = createUser.createUser(adminSession,
createUniqueName("group"), "testPwd", "testPwd",
+ Collections.singletonMap("prop1",
"value1"), new ArrayList<Modification>());
+ assertNotNull("Expected user2 to not be null", user2);
+
+ group1 = createGroup.createGroup(adminSession,
createUniqueName("group"),
+ Collections.singletonMap("prop1",
"value1"), new ArrayList<Modification>());
+ assertNotNull("Expected group1 to not be null", group1);
+
+ String [] principalIds = new String[] { user2.getID(),
group1.getID() };
+
+ // initially user can't do the operation
+ for (String pid : principalIds) {
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid));
+ }
+
+ // start with only read rights
+ Map<String, String> privileges = new HashMap<>();
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_READ), "granted");
+ modifyAce.modifyAce(adminSession, user2.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ for (String pid : principalIds) {
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty,
PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.removeProperty));
+ }
+
+ // + grant user management rights
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+ modifyAce.modifyAce(adminSession, user2.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ for (String pid : principalIds) {
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty,
PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.removeProperty));
+ }
+
+
+ // grant rights to only remove properties
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_ADD_PROPERTIES), "none");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_ALTER_PROPERTIES), "none");
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_ADD_CHILD_NODES), "none");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_REMOVE_PROPERTIES), "granted");
+ modifyAce.modifyAce(adminSession, user2.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ for (String pid : principalIds) {
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty,
PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.removeProperty));
+ }
+
+ // verify that the user can actually delete property
+ Map<String, Object> propsMap = new HashMap<>();
+ propsMap.put("prop1@Delete", "value1");
+ try {
+ updateUser.updateUser(user1Session,
user2.getID(), propsMap, new ArrayList<>());
+ updateGroup.updateGroup(user1Session,
group1.getID(), propsMap, new ArrayList<>());
+ assertTrue("Expected pending changes in the jcr
session", user1Session.hasPendingChanges());
+ user1Session.save();
+ } catch (RepositoryException e) {
+ logger.error("Did not expect
RepositoryException when deleting property: " + e.getMessage(), e);
+ fail("Did not expect RepositoryException when
deleting property: " + e.getMessage());
+ }
+ // verify that the user can not add nested property
+ propsMap = new HashMap<>();
+ propsMap.put("nested/prop2", "value2");
+ try {
+ updateUser.updateUser(user1Session,
user2.getID(), propsMap, new ArrayList<>());
+ updateGroup.updateGroup(user1Session,
group1.getID(), propsMap, new ArrayList<>());
+ assertTrue("Expected pending changes in the jcr
session", user1Session.hasPendingChanges());
+ user1Session.save();
+ fail("Expected AccessDenied exception when
adding nested property");
+ } catch (RepositoryException e) {
+ // expected
+ user1Session.refresh(false);
+ }
+
+
+
+ // grant rights to only alter (non-nested) properties
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_ADD_PROPERTIES), "granted");
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_ADD_CHILD_NODES), "none");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_REMOVE_PROPERTIES), "none");
+ modifyAce.modifyAce(adminSession, user2.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ for (String pid : principalIds) {
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty,
PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.removeProperty));
+ }
+
+ // verify that the user can actually add property
+ propsMap = new HashMap<>();
+ propsMap.put("prop1", "value1");
+ try {
+ updateUser.updateUser(user1Session,
user2.getID(), propsMap, new ArrayList<>());
+ updateGroup.updateGroup(user1Session,
group1.getID(), propsMap, new ArrayList<>());
+ assertTrue("Expected pending changes in the jcr
session", user1Session.hasPendingChanges());
+ user1Session.save();
+ } catch (RepositoryException e) {
+ logger.error("Did not expect
RepositoryException when adding property: " + e.getMessage(), e);
+ fail("Did not expect RepositoryException when
adding property: " + e.getMessage());
+ }
+ // verify that the user can not add nested property
+ propsMap.put("nested/prop2", "value2");
+ try {
+ updateUser.updateUser(user1Session,
user2.getID(), propsMap, new ArrayList<>());
+ updateGroup.updateGroup(user1Session,
group1.getID(), propsMap, new ArrayList<>());
+ assertTrue("Expected pending changes in the jcr
session", user1Session.hasPendingChanges());
+ user1Session.save();
+ fail("Expected AccessDenied exception when
adding nested property");
+ } catch (RepositoryException e) {
+ // expected
+ user1Session.refresh(false);
+ }
+
+
+
+ // grant rights to alter (non-nested or nested)
properties
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_ADD_PROPERTIES), "granted");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_ALTER_PROPERTIES), "granted");
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_ADD_CHILD_NODES), "granted");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_REMOVE_PROPERTIES), "none");
+ modifyAce.modifyAce(adminSession, user2.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ for (String pid : principalIds) {
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty,
PropertyUpdateTypes.removeProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty));
+ assertFalse("Should not be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.removeProperty));
+ }
+
+ // verify that the user can actually add property and
nested property
+ propsMap = new HashMap<>();
+ propsMap.put("prop1", "value1");
+ propsMap.put("nested/prop2", "value2");
+ try {
+ updateUser.updateUser(user1Session,
user2.getID(), propsMap, new ArrayList<>());
+ updateGroup.updateGroup(user1Session,
group1.getID(), propsMap, new ArrayList<>());
+ assertTrue("Expected pending changes in the jcr
session", user1Session.hasPendingChanges());
+ user1Session.save();
+ } catch (RepositoryException e) {
+ logger.error("Did not expect
RepositoryException when adding properties: " + e.getMessage(), e);
+ fail("Did not expect RepositoryException when
adding properties: " + e.getMessage());
+ }
+
+
+
+ // grant rights to alter (non-nested or nested)
properties and remove properties
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_ADD_PROPERTIES), "granted");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_ALTER_PROPERTIES), "granted");
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_ADD_CHILD_NODES), "granted");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_REMOVE_PROPERTIES), "granted");
+ modifyAce.modifyAce(adminSession, user2.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ for (String pid : principalIds) {
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty,
PropertyUpdateTypes.removeProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.addNestedProperty));
+ assertTrue("Should be allowed to update
properties for: " + pid,
+
privilegesInfo.canUpdateProperties(user1Session, pid,
PropertyUpdateTypes.removeProperty));
+ }
+
+ // verify that the user can actually add property and
nested property
+ propsMap = new HashMap<>();
+ propsMap.put("prop3", "value3");
+ propsMap.put("nested/prop4", "value4");
+ propsMap.put("prop1@Delete", "value1");
+ propsMap.put("nested/prop2@Delete", "value1");
+ try {
+ updateUser.updateUser(user1Session,
user2.getID(), propsMap, new ArrayList<>());
+ updateGroup.updateGroup(user1Session,
group1.getID(), propsMap, new ArrayList<>());
+ assertTrue("Expected pending changes in the jcr
session", user1Session.hasPendingChanges());
+ user1Session.save();
+ } catch (RepositoryException e) {
+ logger.error("Did not expect
RepositoryException when adding or deleting properties: " + e.getMessage(), e);
+ fail("Did not expect RepositoryException when
adding or deleting properties: " + e.getMessage());
+ }
+ } finally {
+ if (user2 != null) {
+ deleteUser.deleteUser(adminSession,
user2.getID(), new ArrayList<>());
+ }
+ }
+ }
+
+ /**
+ * Checks whether the current user has been granted privileges
+ * to remove the specified user or group.
+ */
+ @Test
+ public void canRemove() throws Exception {
+ assertNotNull("Expected privilegesInfo to not be null",
privilegesInfo);
+
+ workaroundMissingGroupsPath();
+
+ User user2 = null;
+ Group group1 = null;
+ try {
+ user2 = createUser.createUser(adminSession,
createUniqueName("user"), "testPwd", "testPwd",
+ Collections.singletonMap("prop1",
"value1"), new ArrayList<Modification>());
+ assertNotNull("Expected user2 to not be null", user2);
+
+ group1 = createGroup.createGroup(adminSession,
createUniqueName("group"),
+ Collections.singletonMap("prop1",
"value1"), new ArrayList<Modification>());
+ assertNotNull("Expected group1 to not be null", group1);
+
+ // initially user can't do the operations
+ assertFalse("Should not be allowed to remove user",
+ privilegesInfo.canRemove(user1Session,
user2.getID()));
+ assertFalse("Should not be allowed to remove group",
+ privilegesInfo.canRemove(user1Session,
group1.getID()));
+
+ // grant user1 rights to user2 profile
+ Map<String, String> privileges = new HashMap<>();
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_READ), "granted");
+ modifyAce.modifyAce(adminSession, user2.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ assertFalse("Should not be allowed to remove user",
+ privilegesInfo.canRemove(user1Session,
user2.getID()));
+ assertFalse("Should not be allowed to remove group",
+ privilegesInfo.canRemove(user1Session,
group1.getID()));
+
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+ modifyAce.modifyAce(adminSession, user2.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ assertTrue("Should be allowed to remove user",
+ privilegesInfo.canRemove(user1Session,
user2.getID()));
+ assertTrue("Should be allowed to remove group",
+ privilegesInfo.canRemove(user1Session,
group1.getID()));
+
+ // verify that the user can actually delete the user
+ String user2Id = user2.getID();
+ deleteUser.deleteUser(user1Session, user2Id, new
ArrayList<>());
+ user2 = null;
+ // verify the user is no longer there
+ UserManager um =
AccessControlUtil.getUserManager(user1Session);
+ assertNull("Expected user to be gone: " + user2Id,
um.getAuthorizable(user2Id));
+
+ // verify that the user can actually delete the group
+ String group1Id = group1.getID();
+ deleteGroup.deleteGroup(user1Session, group1Id, new
ArrayList<>());
+ group1 = null;
+ assertNull("Expected group to be gone: " + group1Id,
um.getAuthorizable(group1Id));
+ } finally {
+ if (user2 != null) {
+ deleteUser.deleteUser(adminSession,
user2.getID(), new ArrayList<>());
+ }
+ if (group1 != null) {
+ deleteGroup.deleteGroup(adminSession,
group1.getID(), new ArrayList<>());
+ }
+ }
+ }
+
+ /**
+ * Checks whether the current user has been granted privileges
+ * to update the membership of the specified group.
+ */
+ @Test
+ public void canUpdateGroupMembers() throws Exception {
+ assertNotNull("Expected privilegesInfo to not be null",
privilegesInfo);
+
+ workaroundMissingGroupsPath();
+
+ Group group1 = null;
+ try {
+ group1 = createGroup.createGroup(adminSession,
createUniqueName("group"),
+ Collections.singletonMap("prop1",
"value1"), new ArrayList<Modification>());
+ assertNotNull("Expected group1 to not be null", group1);
+
+ // initially user can't do the operations
+ assertFalse("Should not be allowed to update group
members",
+
privilegesInfo.canUpdateGroupMembers(user1Session, group1.getID()));
+
+ // grant user1 rights to group1 profile
+ Map<String, String> privileges = new HashMap<>();
+ privileges.put(String.format("privilege@%s",
Privilege.JCR_READ), "granted");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ assertFalse("Should not be allowed to update group
members",
+
privilegesInfo.canUpdateGroupMembers(user1Session, group1.getID()));
+
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+ modifyAce.modifyAce(adminSession, group1.getPath(),
user1.getID(),
+ privileges,
+ "first");
+ assertTrue("Should be allowed to update group members",
+
privilegesInfo.canUpdateGroupMembers(user1Session, group1.getID()));
+
+ // verify that the user can actually change the group
members
+ try {
+ Map<String, Object> propsMap = new HashMap<>();
+ propsMap.put(":member", user1.getID());
+ updateGroup.updateGroup(user1Session,
group1.getID(), propsMap, new ArrayList<>());
+ assertTrue("Expected pending changes in the jcr
session", user1Session.hasPendingChanges());
+ user1Session.save();
+ } catch (RepositoryException e) {
+ logger.error("Did not expect
RepositoryException when adding member to group: " + e.getMessage(), e);
+ fail("Did not expect RepositoryException when
adding member to group: " + e.getMessage());
+ }
+ } finally {
+ if (group1 != null) {
+ deleteGroup.deleteGroup(adminSession,
group1.getID(), new ArrayList<>());
+ }
+ }
+ }
+
+}
diff --git
a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/UserManagerTestSupport.java
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/UserManagerTestSupport.java
new file mode 100644
index 0000000..fed4cb9
--- /dev/null
+++
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/UserManagerTestSupport.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+import static org.apache.sling.testing.paxexam.SlingOptions.sling;
+import static
org.apache.sling.testing.paxexam.SlingOptions.slingQuickstartOakTar;
+import static org.ops4j.pax.exam.CoreOptions.composite;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.vmOption;
+
+import org.apache.sling.testing.paxexam.SlingOptions;
+import org.apache.sling.testing.paxexam.TestSupport;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.options.ModifiableCompositeOption;
+import org.ops4j.pax.exam.options.extra.VMOption;
+
+/**
+ * Base class for UserManager related paxexam tests
+ */
+public abstract class UserManagerTestSupport extends TestSupport {
+
+ public ModifiableCompositeOption baseConfiguration() {
+ final Option usermanager = mavenBundle()
+ .groupId("org.apache.sling")
+
.artifactId("org.apache.sling.jcr.jackrabbit.usermanager")
+
.version(SlingOptions.versionResolver.getVersion("org.apache.sling",
"org.apache.sling.jcr.jackrabbit.usermanager"));
+ return composite(
+ super.baseConfiguration(),
+ optionalRemoteDebug(),
+ quickstart(),
+ sling(),
+ // Sling JCR UserManager
+ testBundle("bundle.filename"),
+
mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.jcr.jackrabbit.accessmanager").versionAsInProject(),
+ junitBundles()
+ ).remove(
+ usermanager
+ );
+ }
+
+ /**
+ * Optionally configure remote debugging on the port supplied by the
"debugPort"
+ * system property.
+ */
+ protected ModifiableCompositeOption optionalRemoteDebug() {
+ VMOption option = null;
+ String property = System.getProperty("debugPort");
+ if (property != null) {
+ option =
vmOption(String.format("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%s",
property));
+ }
+ return composite(option);
+ }
+ protected ModifiableCompositeOption quickstart() {
+ final int httpPort = findFreePort();
+ final String workingDirectory = workingDirectory();
+ return slingQuickstartOakTar(workingDirectory, httpPort);
+ }
+
+}