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 0b9fec1 SLING-9808 Add configuration option to always allow users to
change their own password
0b9fec1 is described below
commit 0b9fec191d3990fa025092b59aeec8029e8b1f6d
Author: Eric Norman <[email protected]>
AuthorDate: Sat Oct 10 16:12:01 2020 -0700
SLING-9808 Add configuration option to always allow users to change
their own password
---
.../impl/post/ChangeUserPasswordServlet.java | 121 +++++++---
.../sling/jcr/jackrabbit/usermanager/it/Retry.java | 69 ++++++
.../usermanager/it/UserManagerTestSupport.java | 72 +++++-
.../usermanager/it/post/ChangeUserPasswordIT.java | 251 +++++++++++++++++++++
4 files changed, 473 insertions(+), 40 deletions(-)
diff --git
a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/ChangeUserPasswordServlet.java
b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/ChangeUserPasswordServlet.java
index 0e4b38b..639e4a8 100644
---
a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/ChangeUserPasswordServlet.java
+++
b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/ChangeUserPasswordServlet.java
@@ -21,18 +21,22 @@ import java.util.Map;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
import javax.servlet.Servlet;
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.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceNotFoundException;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.jackrabbit.usermanager.ChangeUserPassword;
import
org.apache.sling.jackrabbit.usermanager.impl.resource.AuthorizableResourceProvider;
+import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.jcr.base.util.AccessControlUtil;
import org.apache.sling.servlets.post.Modification;
import org.apache.sling.servlets.post.PostResponse;
@@ -43,6 +47,9 @@ import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -86,18 +93,31 @@ import org.slf4j.LoggerFactory;
@Component(service = {Servlet.class, ChangeUserPassword.class},
property = {
- "sling.servlet.resourceTypes=sling/user",
- "sling.servlet.methods=POST",
- "sling.servlet.selectors=changePassword",
- AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=EEE MMM dd yyyy HH:mm:ss 'GMT'Z",
- AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=yyyy-MM-dd'T'HH:mm:ss.SSSZ",
- AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=yyyy-MM-dd'T'HH:mm:ss",
- AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=yyyy-MM-dd",
- AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=dd.MM.yyyy HH:mm:ss",
- AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=dd.MM.yyyy",
- ChangeUserPasswordServlet.PAR_USER_ADMIN_GROUP_NAME
+ "=" + ChangeUserPasswordServlet.DEFAULT_USER_ADMIN_GROUP_NAME
+ "sling.servlet.resourceTypes=sling/user",
+ "sling.servlet.methods=POST",
+ "sling.servlet.selectors=changePassword",
+ AbstractAuthorizablePostServlet.PROP_DATE_FORMAT + "=EEE
MMM dd yyyy HH:mm:ss 'GMT'Z",
+ AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=yyyy-MM-dd'T'HH:mm:ss.SSSZ",
+ AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=yyyy-MM-dd'T'HH:mm:ss",
+ AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=yyyy-MM-dd",
+ AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=dd.MM.yyyy HH:mm:ss",
+ AbstractAuthorizablePostServlet.PROP_DATE_FORMAT +
"=dd.MM.yyyy"
})
+@Designate(ocd=ChangeUserPasswordServlet.Config.class)
public class ChangeUserPasswordServlet extends AbstractAuthorizablePostServlet
implements ChangeUserPassword {
+
+ @ObjectClassDefinition(name ="Apache Sling Change User Password")
+ public @interface Config {
+
+ @AttributeDefinition(name = "User Admin Group Name",
+ description = "Specifies the name of the group whose members
are allowed to reset the password of another user.")
+ String user_admin_group_name() default DEFAULT_USER_ADMIN_GROUP_NAME;
+
+ @AttributeDefinition(name = "Always Allow Self Password Change",
+ description = "Specifies whether a user is allowed to change
their own password even if they haven't been granted the rep:userManagement
privilege.")
+ boolean alwaysAllowSelfChangePassword() default false;
+ }
+
private static final long serialVersionUID = 1923614318474654502L;
/**
@@ -121,7 +141,13 @@ public class ChangeUserPasswordServlet extends
AbstractAuthorizablePostServlet i
private String userAdminGroupName = DEFAULT_USER_ADMIN_GROUP_NAME;
- // ---------- SCR integration ---------------------------------------------
+ private boolean alwaysAllowSelfChangePassword = true;
+
+ /**
+ * The JCR Repository we access to resolve resources
+ */
+ @Reference
+ private SlingRepository repository;
/**
* Activates this component.
@@ -133,6 +159,8 @@ public class ChangeUserPasswordServlet extends
AbstractAuthorizablePostServlet i
protected void activate(final Map<String, Object> props) {
super.activate(props);
+ alwaysAllowSelfChangePassword =
OsgiUtil.toBoolean(props.get("alwaysAllowSelfChangePassword"), false);
+
this.userAdminGroupName =
OsgiUtil.toString(props.get(PAR_USER_ADMIN_GROUP_NAME),
DEFAULT_USER_ADMIN_GROUP_NAME);
log.debug("User Admin Group Name {}", this.userAdminGroupName);
@@ -147,25 +175,25 @@ public class ChangeUserPasswordServlet extends
AbstractAuthorizablePostServlet i
/**
* Overridden since the @Reference annotation is not inherited from the
super method
*
- * @see
org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#bindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator,
java.util.Map)
- */
- @Override
+ * @see
org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#bindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator,
java.util.Map)
+ */
+ @Override
@Reference(service = PostResponseCreator.class,
- cardinality = ReferenceCardinality.MULTIPLE,
- policy = ReferencePolicy.DYNAMIC)
- protected void bindPostResponseCreator(PostResponseCreator creator,
Map<String, Object> properties) {
- super.bindPostResponseCreator(creator, properties);
- }
-
- /* (non-Javadoc)
- * @see
org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#unbindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator,
java.util.Map)
- */
- @Override
- protected void unbindPostResponseCreator(PostResponseCreator creator,
Map<String, Object> properties) {
- super.unbindPostResponseCreator(creator, properties);
- }
-
- /*
+ cardinality = ReferenceCardinality.MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC)
+ protected void bindPostResponseCreator(PostResponseCreator creator,
Map<String, Object> properties) {
+ super.bindPostResponseCreator(creator, properties);
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.sling.jackrabbit.usermanager.impl.post.AbstractPostServlet#unbindPostResponseCreator(org.apache.sling.servlets.post.PostResponseCreator,
java.util.Map)
+ */
+ @Override
+ protected void unbindPostResponseCreator(PostResponseCreator creator,
Map<String, Object> properties) {
+ super.unbindPostResponseCreator(creator, properties);
+ }
+
+ /*
* (non-Javadoc)
* @see
*
org.apache.sling.jackrabbit.usermanager.post.AbstractAuthorizablePostServlet
@@ -174,7 +202,7 @@ public class ChangeUserPasswordServlet extends
AbstractAuthorizablePostServlet i
*/
@Override
protected void handleOperation(SlingHttpServletRequest request,
- PostResponse response, List<Modification> changes)
+ PostResponse response, List<Modification> changes)
throws RepositoryException {
Resource resource = request.getResource();
@@ -253,7 +281,38 @@ public class ChangeUserPasswordServlet extends
AbstractAuthorizablePostServlet i
if (oldPassword != null && oldPassword.length() > 0) {
// verify old password
- user.changePassword(newPassword, oldPassword);
+ if (alwaysAllowSelfChangePassword &&
jcrSession.getUserID().equals(name)) {
+ // first check if the current user has enough permissions to
do this without
+ // the aid of a service session
+ AccessControlManager acm =
jcrSession.getAccessControlManager();
+ boolean hasRights = acm.hasPrivileges(authorizable.getPath(),
new Privilege[] {
+
acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT)
+ });
+
+ if (hasRights) {
+ // we are good to do this without an extra service session
+ user.changePassword(newPassword, oldPassword);
+ } else {
+ // the current user doesn't have enough permissions, so
we'll need do
+ // do the work on their behalf as a service user
+ Session svcSession = null;
+ try {
+ svcSession = repository.loginAdministrative(null);
+ UserManager um =
AccessControlUtil.getUserManager(svcSession);
+ User user2 = (User) um.getAuthorizable(name);
+ user2.changePassword(newPassword, oldPassword);
+ if (svcSession.hasPendingChanges()) {
+ svcSession.save();
+ }
+ } finally {
+ if (svcSession != null) {
+ svcSession.logout();
+ }
+ }
+ }
+ } else {
+ user.changePassword(newPassword, oldPassword);
+ }
} else {
user.changePassword(newPassword);
}
diff --git
a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/Retry.java
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/Retry.java
new file mode 100644
index 0000000..f2454c3
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/Retry.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.jcr.jackrabbit.usermanager.it;
+
+import static org.junit.Assert.fail;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Simple Retry loop for tests */
+public abstract class Retry {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ private long timeoutMsec;
+ private long nextIterationDelay;
+
+ public Retry(long timeoutMsec, long nextIterationDelay) throws
InterruptedException {
+ this(timeoutMsec, nextIterationDelay, true);
+ }
+
+ public Retry(long timeoutMsec, long nextIterationDelay, boolean
autorun) throws InterruptedException {
+ this.timeoutMsec = timeoutMsec;
+ this.nextIterationDelay = nextIterationDelay;
+ if (autorun) {
+ run();
+ }
+ }
+
+ protected void run() throws InterruptedException {
+ final long timeout = System.currentTimeMillis() + timeoutMsec;
+ Throwable lastT = null;
+ while (System.currentTimeMillis() < timeout) {
+ try {
+ lastT = null;
+ exec();
+ break;
+ } catch(Throwable t) {
+ if (logger.isDebugEnabled()) {
+ logger.warn("exec failed: " + t.getMessage(), t);
+ } else {
+ logger.warn("exec failed: " + t.getMessage());
+ }
+ lastT = t;
+ Thread.sleep(nextIterationDelay);
+ }
+ }
+
+ if (lastT != null) {
+ fail("Failed after " + timeoutMsec + " msec: " + lastT);
+ }
+ }
+
+ protected abstract void exec() throws Exception;
+}
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
index fed4cb9..11c1440 100644
---
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
@@ -20,27 +20,65 @@ 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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
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 java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
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;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
/**
* Base class for UserManager related paxexam tests
*/
public abstract class UserManagerTestSupport extends TestSupport {
+ /**
+ * Use after using ConfigurationAdmin to change the configuration of
+ * a service and you need to wait for the component to be re-activated
+ * with the new configuration.
+ */
+ public static final class WaitForServiceUpdated extends Retry {
+ private BundleContext bundleContext;
+ private String expectedKey;
+ private Object expectedValue;
+ private Class<?> serviceClass;
+
+ public WaitForServiceUpdated(long timeoutMsec, long
nextIterationDelay, BundleContext bundleContext,
+ Class<?> serviceClass, String expectedKey, Object
expectedValue) throws InterruptedException {
+ super(timeoutMsec, nextIterationDelay, false);
+ this.bundleContext = bundleContext;
+ this.serviceClass = serviceClass;
+ this.expectedKey = expectedKey;
+ this.expectedValue = expectedValue;
+ run();
+ }
+
+ @Override
+ protected void exec() throws Exception {
+ ServiceReference<?> serviceReference =
bundleContext.getServiceReference(serviceClass);
+ assertNotNull(serviceReference);
+ assertEquals(expectedValue,
serviceReference.getProperty(expectedKey));
+ }
+ }
+
+
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"));
+ .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(),
@@ -60,12 +98,12 @@ public abstract class UserManagerTestSupport extends
TestSupport {
* 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);
+ 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();
@@ -73,4 +111,20 @@ public abstract class UserManagerTestSupport extends
TestSupport {
return slingQuickstartOakTar(workingDirectory, httpPort);
}
+ protected Dictionary<String, Object> replaceConfigProp(Dictionary<String,
Object> originalProps, String newPropKey, Object newPropValue) {
+ Hashtable<String, Object> newProps = new Hashtable<>();
+ if (originalProps != null) {
+ Enumeration<String> keys = originalProps.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ Object value = originalProps.get(key);
+ newProps.put(key, value);
+ }
+ }
+
+ newProps.put(newPropKey, newPropValue);
+
+ return newProps;
+ }
+
}
diff --git
a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/ChangeUserPasswordIT.java
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/ChangeUserPasswordIT.java
new file mode 100644
index 0000000..2b749bd
--- /dev/null
+++
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/ChangeUserPasswordIT.java
@@ -0,0 +1,251 @@
+/*
+ * 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.post;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+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.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.inject.Inject;
+import javax.jcr.AccessDeniedException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.security.Privilege;
+
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.sling.jackrabbit.usermanager.ChangeUserPassword;
+import org.apache.sling.jackrabbit.usermanager.CreateUser;
+import org.apache.sling.jackrabbit.usermanager.DeleteUser;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.jackrabbit.accessmanager.DeleteAces;
+import org.apache.sling.jcr.jackrabbit.accessmanager.ModifyAce;
+import org.apache.sling.jcr.jackrabbit.usermanager.it.UserManagerTestSupport;
+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.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Basic test of ChangeUserPassword component
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class ChangeUserPasswordIT 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
+ protected ConfigurationAdmin configAdmin;
+
+ @Inject
+ private CreateUser createUser;
+
+ @Inject
+ private ModifyAce modifyAce;
+
+ @Inject
+ private DeleteAces deleteAces;
+
+ @Inject
+ private DeleteUser deleteUser;
+
+ @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);
+
+ //change the ACE for the user home folder to the minimum privileges
+ // and without rep:userManagement
+ deleteAces.deleteAces(adminSession, user1.getPath(), new String[]
{user1.getID()});
+ Map<String, String> privileges = new HashMap<>();
+ privileges.put(String.format("privilege@%s", Privilege.JCR_READ),
"granted");
+ privileges.put(String.format("privilege@%s",
PrivilegeConstants.REP_ALTER_PROPERTIES), "granted");
+ modifyAce.modifyAce(adminSession, user1.getPath(), user1.getID(),
+ privileges,
+ "first");
+ if (adminSession.hasPendingChanges()) {
+ adminSession.save();
+ }
+ }
+
+ @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());
+ }
+
+ /**
+ * SLING-9808 test changing password when user doesn't have
rep:userManagement privilege
+ */
+ @Test
+ public void changePasswordAsSelfGranted() throws Exception {
+ org.osgi.service.cm.Configuration configuration =
configAdmin.getConfiguration("org.apache.sling.jackrabbit.usermanager.impl.post.ChangeUserPasswordServlet",
null);
+ Dictionary<String, Object> originalServiceProps =
configuration.getProperties();
+ ServiceReference<ChangeUserPassword> serviceReference = null;
+ try {
+ // update the service configuration to ensure the option is enabled
+ Dictionary<String, Object> newServiceProps =
replaceConfigProp(originalServiceProps, "alwaysAllowSelfChangePassword",
Boolean.TRUE);
+ configuration.update(newServiceProps);
+ new WaitForServiceUpdated(5000, 100, bundleContext,
ChangeUserPassword.class,
+ "alwaysAllowSelfChangePassword", Boolean.TRUE);
+
+ serviceReference =
bundleContext.getServiceReference(ChangeUserPassword.class);
+ assertEquals(Boolean.TRUE,
serviceReference.getProperty("alwaysAllowSelfChangePassword"));
+ ChangeUserPassword changeUserPassword =
bundleContext.getService(serviceReference);
+ assertNotNull(changeUserPassword);
+ changeUserPassword.changePassword(user1Session,
+ user1.getID(),
+ "testPwd",
+ "testPwdChanged",
+ "testPwdChanged",
+ new ArrayList<>());
+ try {
+ user1Session.save();
+ } catch (AccessDeniedException e) {
+ logger.error("Did not expect AccessDeniedException when
changing user passsword: " + e.getMessage(), e);
+ fail("Did not expect AccessDeniedException when changing user
passsword: " + e.getMessage());
+ }
+ } finally {
+ if (serviceReference != null) {
+ // done with this.
+ bundleContext.ungetService(serviceReference);
+ }
+
+ //put the original config back
+ configuration.update(originalServiceProps);
+ new WaitForServiceUpdated(5000, 100, bundleContext,
ChangeUserPassword.class, "alwaysAllowSelfChangePassword",
+ originalServiceProps == null ? null
:originalServiceProps.get("alwaysAllowSelfChangePassword"));
+ }
+ }
+
+ /**
+ * SLING-9808 test changing password when user doesn't have
rep:userManagement privilege
+ */
+ @Test
+ public void changePasswordAsSelfDenied() throws Exception {
+ org.osgi.service.cm.Configuration configuration =
configAdmin.getConfiguration("org.apache.sling.jackrabbit.usermanager.impl.post.ChangeUserPasswordServlet",
null);
+ Dictionary<String, Object> originalServiceProps =
configuration.getProperties();
+ ServiceReference<ChangeUserPassword> serviceReference = null;
+ try {
+ // update the service configuration to ensure the option is
disabled
+ Dictionary<String, Object> newServiceProps =
replaceConfigProp(originalServiceProps, "alwaysAllowSelfChangePassword",
Boolean.FALSE);
+ configuration.update(newServiceProps);
+ new WaitForServiceUpdated(5000, 100, bundleContext,
ChangeUserPassword.class,
+ "alwaysAllowSelfChangePassword", Boolean.FALSE);
+
+ serviceReference =
bundleContext.getServiceReference(ChangeUserPassword.class);
+ assertEquals(Boolean.FALSE,
serviceReference.getProperty("alwaysAllowSelfChangePassword"));
+ ChangeUserPassword changeUserPassword =
bundleContext.getService(serviceReference);
+ assertNotNull(changeUserPassword);
+ changeUserPassword.changePassword(user1Session,
+ user1.getID(),
+ "testPwd",
+ "testPwdChanged",
+ "testPwdChanged",
+ new ArrayList<>());
+ assertTrue(user1Session.hasPendingChanges());
+ try {
+ user1Session.save();
+ fail("Expected an AccessDeniedException when changing user
passsword.");
+ } catch (AccessDeniedException e) {
+ // expected an Access is denied exception
+ Throwable cause = e.getCause();
+ assertTrue(cause instanceof CommitFailedException);
+ assertEquals("OakAccess0000: Access denied",
cause.getMessage());
+ }
+ } finally {
+ if (serviceReference != null) {
+ // done with this.
+ bundleContext.ungetService(serviceReference);
+ }
+
+ //put the original config back
+ configuration.update(originalServiceProps);
+ new WaitForServiceUpdated(5000, 100, bundleContext,
ChangeUserPassword.class, "alwaysAllowSelfChangePassword",
+ originalServiceProps == null ? null
:originalServiceProps.get("alwaysAllowSelfChangePassword"));
+ }
+ }
+
+}