SLING-10902 ability to generate principalName from a hint (#7)
diff --git a/pom.xml b/pom.xml
index 5ea3306..b3e7515 100644
--- a/pom.xml
+++ b/pom.xml
@@ -122,7 +122,7 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.api</artifactId>
- <version>2.23.7-SNAPSHOT</version>
+ <version>2.24.0</version>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/CreateGroup.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/CreateGroup.java
index 6899818..bf16611 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/CreateGroup.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/CreateGroup.java
@@ -42,7 +42,7 @@
* Create a new group for the repository
*
* @param jcrSession the JCR session of the user creating the group
- * @param name The name of the new group (required)
+ * @param name The name of the new group. If null or empty, the name is calculated from the supplied properties (per SLING-10902).
* @param properties Extra properties to update on the group. The entry values should be either a String or String[] (optional)
* @param changes The list of changes for this operation (optional)
* @return the group that was created
@@ -53,5 +53,22 @@
Map<String, ?> properties,
List<Modification> changes
) throws RepositoryException;
-
+
+ /**
+ * Create a new group for the repository. The name is calculated from the
+ * supplied properties (per SLING-10902).
+ *
+ * @param jcrSession the JCR session of the user creating the group
+ * @param properties Extra properties to update on the group. The entry values should be either a String or String[] (optional)
+ * @param changes The list of changes for this operation (optional)
+ * @return the group that was created
+ * @throws RepositoryException if group can't be created
+ */
+ public default Group createGroup(Session jcrSession,
+ Map<String, ?> properties,
+ List<Modification> changes
+ ) throws RepositoryException {
+ return createGroup(jcrSession, null, properties, changes);
+ }
+
}
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/CreateUser.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/CreateUser.java
index 4da99fa..28e1118 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/CreateUser.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/CreateUser.java
@@ -42,7 +42,7 @@
* Create a new user for the repository
*
* @param jcrSession the JCR session of the user creating the user
- * @param name The name of the new user (required)
+ * @param name The name of the new user. If null or empty, the name is calculated from the supplied properties (per SLING-10902).
* @param password The password of the new user (required)
* @param passwordConfirm The password of the new user again (required)
* @param properties Extra properties to update on the user. The entry values should be either a String or String[] (optional)
@@ -57,5 +57,25 @@
Map<String, ?> properties,
List<Modification> changes
) throws RepositoryException;
-
+
+ /**
+ * Create a new user for the repository. The name is calculated from the
+ * supplied properties (per SLING-10902).
+ *
+ * @param jcrSession the JCR session of the user creating the user
+ * @param password The password of the new user (required)
+ * @param passwordConfirm The password of the new user again (required)
+ * @param properties Extra properties to update on the user. The entry values should be either a String or String[] (optional)
+ * @param changes The list of changes for this operation (optional)
+ * @return the user that was created
+ * @throws RepositoryException if user can't be created
+ */
+ public default User createUser(Session jcrSession,
+ String password,
+ String passwordConfirm,
+ Map<String, ?> properties,
+ List<Modification> changes
+ ) throws RepositoryException {
+ return createUser(jcrSession, null, password, passwordConfirm, properties, changes);
+ }
}
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/PrincipalNameFilter.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/PrincipalNameFilter.java
new file mode 100644
index 0000000..6437f7d
--- /dev/null
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/PrincipalNameFilter.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Service interface which allows for filtering what characters are allowed
+ * in a generated principal name
+ */
+public interface PrincipalNameFilter {
+
+ /**
+ * Filter the invalid characters out of the provided principal name candidate
+ * @param principalName the candidate principal name
+ * @return the filtered value
+ */
+ String filter(String principalName);
+
+}
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/PrincipalNameGenerator.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/PrincipalNameGenerator.java
new file mode 100644
index 0000000..6453d2b
--- /dev/null
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/PrincipalNameGenerator.java
@@ -0,0 +1,59 @@
+/*
+ * 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 java.util.Map;
+
+import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
+
+/**
+ * Service interface which allows for custom principal name generation
+ */
+public interface PrincipalNameGenerator {
+ public static class NameInfo {
+ private String principalName;
+ private boolean makeUnique = false;
+
+ public NameInfo(String principalName, boolean makeUnique) {
+ this.principalName = principalName;
+ this.makeUnique = makeUnique;
+ }
+
+ public String getPrincipalName() {
+ return principalName;
+ }
+
+ public boolean isMakeUnique() {
+ return makeUnique;
+ }
+ }
+
+ /**
+ * Get the to-be-created principal name candidate from the request.
+ *
+ * @param parameters the current request parameters map
+ * @param type the type of principal
+ * @param principalNameFilter for filtering what characters are allowed in a name
+ * @param defaultPrincipalNameGenerator the default principal name generator
+ *
+ * @return the info about the principal name to be created or null if unable to do so
+ */
+ public NameInfo getPrincipalName(Map<String, ?> parameters, AuthorizableType type,
+ PrincipalNameFilter principalNameFilter,
+ PrincipalNameGenerator defaultPrincipalNameGenerator);
+
+}
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/AbstractAuthorizablePostServlet.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/AbstractAuthorizablePostServlet.java
index 9d1b7c9..d214143 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/AbstractAuthorizablePostServlet.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/AbstractAuthorizablePostServlet.java
@@ -19,10 +19,14 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.security.SecureRandom;
import java.util.Calendar;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -33,10 +37,16 @@
import javax.jcr.ValueFactory;
import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
import org.apache.sling.api.SlingIOException;
import org.apache.sling.api.request.RequestParameter;
import org.apache.sling.commons.osgi.OsgiUtil;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameFilter;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator.NameInfo;
import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
+import org.apache.sling.jcr.base.util.AccessControlUtil;
import org.apache.sling.servlets.post.Modification;
import org.apache.sling.servlets.post.SlingPostConstants;
import org.apache.sling.servlets.post.impl.helper.DateParser;
@@ -52,10 +62,29 @@
AbstractPostServlet {
private static final long serialVersionUID = -5918670409789895333L;
+ private static final class PrincipalNameGeneratorHolder {
+ private final PrincipalNameGenerator generator;
+ private final int ranking;
+
+ private PrincipalNameGeneratorHolder(PrincipalNameGenerator generator, int ranking) {
+ this.generator = generator;
+ this.ranking = ranking;
+ }
+
+ public PrincipalNameGenerator getGenerator() {
+ return generator;
+ }
+
+ }
+
+ protected static final String RP_NODE_NAME_VALUE_FROM = String.format("%s%s", SlingPostConstants.RP_NODE_NAME, SlingPostConstants.VALUE_FROM_SUFFIX);
+ protected static final String RP_NODE_NAME_HINT_VALUE_FROM = String.format("%s%s", SlingPostConstants.RP_NODE_NAME_HINT, SlingPostConstants.VALUE_FROM_SUFFIX);
+
public static final String PROP_DATE_FORMAT = "servlet.post.dateFormats";
private static final Logger LOG = LoggerFactory.getLogger(AbstractAuthorizablePostServlet.class);
+ private final SecureRandom randomCollisionIndex = new SecureRandom();
private transient DateParser dateParser;
protected transient SystemUserManagerPaths systemUserManagerPaths;
@@ -64,6 +93,112 @@
this.systemUserManagerPaths = sump;
}
+ /**
+ * The principal name generators
+ */
+ protected transient LinkedList<PrincipalNameGeneratorHolder> principalNameGenerators = new LinkedList<>();
+
+ /**
+ * The optional principal name filter
+ */
+ protected transient PrincipalNameFilter principalNameFilter;
+
+ /**
+ * Bind a new principal name generator
+ */
+// @Reference(service = PrincipalNameGenerator.class)
+ protected void bindPrincipalNameGenerator(final PrincipalNameGenerator generator, final Map<String, Object> properties) {
+ final PrincipalNameGeneratorHolder pngh = new PrincipalNameGeneratorHolder(generator, getRanking(properties));
+ synchronized (principalNameGenerators) {
+ this.principalNameGenerators.add(pngh);
+ Collections.sort(this.principalNameGenerators, (o1, o2) ->
+ Integer.compare(o1.ranking, o2.ranking));
+ }
+ }
+ protected void unbindPrincipalNameGenerator(final PrincipalNameGenerator generator) {
+ synchronized (principalNameGenerators) {
+ principalNameGenerators.removeIf(h -> h.generator == generator);
+ }
+ }
+
+ /**
+ * Bind a new principal name filter
+ */
+// @Reference(service = PrincipalNameFilter.class)
+ protected void bindPrincipalNameFilter(final PrincipalNameFilter filter) {
+ this.principalNameFilter = filter;
+ }
+ protected void unbindPrincipalNameFilter(final PrincipalNameFilter filter) {
+ if (filter != null && filter.equals(this.principalNameFilter)) {
+ this.principalNameFilter = null;
+ }
+ }
+
+ /**
+ * Get or generate the name of the principal being created
+ *
+ * @param request the current request
+ * @return the principal name
+ */
+ protected String getOrGeneratePrincipalName(Session jcrSession, Map<String, ?> properties, AuthorizableType type) throws RepositoryException {
+ String principalName = null;
+ PrincipalNameGenerator defaultPrincipalNameGenerator = null;
+ PrincipalNameGenerator principalNameGenerator = null;
+ synchronized (principalNameGenerators) {
+ if (!principalNameGenerators.isEmpty()) {
+ defaultPrincipalNameGenerator = principalNameGenerators.getFirst().getGenerator();
+ principalNameGenerator = principalNameGenerators.getLast().getGenerator();
+ }
+ }
+ if (principalNameGenerator != null) {
+ NameInfo nameInfo = principalNameGenerator.getPrincipalName(properties, type,
+ principalNameFilter, defaultPrincipalNameGenerator);
+ if (nameInfo == null && defaultPrincipalNameGenerator != null) {
+ // fallback to the default impl
+ nameInfo = defaultPrincipalNameGenerator.getPrincipalName(properties, type,
+ principalNameFilter, defaultPrincipalNameGenerator);
+ }
+ if (nameInfo != null) {
+ principalName = nameInfo.getPrincipalName();
+ if (principalName != null && nameInfo.isMakeUnique()) {
+ // make sure the name is not already used
+ UserManager um = AccessControlUtil.getUserManager(jcrSession);
+
+ // if resulting authorizable exists, add a random suffix until it's not the case
+ // anymore
+ final int MAX_TRIES = 1000;
+ if (um.getAuthorizable(principalName) != null ) {
+ for (int i=0; i < MAX_TRIES; i++) {
+ final int uniqueIndex = randomCollisionIndex.nextInt(9999);
+ String newPrincipalName = principalName + "_" + uniqueIndex;
+ if (um.getAuthorizable(newPrincipalName) == null) {
+ // found unused value, so use it
+ principalName = newPrincipalName;
+ break;
+ }
+ }
+
+ // Give up after MAX_TRIES
+ if (um.getAuthorizable(principalName) != null ) {
+ throw new RepositoryException(
+ "Collision in generated principal names, generated name " + principalName + " already exists");
+ }
+ }
+ }
+ }
+ } else {
+ // fallback to the old behavior
+ Object obj = properties.get(SlingPostConstants.RP_NODE_NAME);
+ if (obj instanceof String[] && Array.getLength(obj) == 1) {
+ principalName = ((String[])obj)[0];
+ } else if (obj instanceof String) {
+ principalName= ((String)obj);
+ }
+ }
+
+ return principalName;
+ }
+
// ---------- SCR Integration ----------------------------------------------
protected void activate(Map<String, Object> props) {
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/AbstractPostServlet.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/AbstractPostServlet.java
index 542baa4..baac95f 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/AbstractPostServlet.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/AbstractPostServlet.java
@@ -459,7 +459,7 @@
this.cachedPostResponseCreators = localCache;
}
- private int getRanking(final Map<String, Object> properties) {
+ protected int getRanking(final Map<String, Object> properties) {
final Object val = properties.get(Constants.SERVICE_RANKING);
return val instanceof Integer ? (Integer)val : 0;
}
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/CreateGroupServlet.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/CreateGroupServlet.java
index 71ec9d4..f8e966e 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/CreateGroupServlet.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/CreateGroupServlet.java
@@ -28,12 +28,15 @@
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
import org.apache.sling.api.SlingHttpServletRequest;
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.PrincipalNameFilter;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator;
import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
import org.apache.sling.jcr.base.util.AccessControlUtil;
import org.apache.sling.servlets.post.Modification;
@@ -47,6 +50,7 @@
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.component.annotations.ReferencePolicyOption;
/**
* <p>
@@ -65,8 +69,16 @@
* </ul>
* <h3>Post Parameters</h3>
* <dl>
- * <dt>:name</dt>
- * <dd>The name of the new group (required)</dd>
+ * <dt>one of these</dt>
+ * <dd>
+ * <ul>
+ * <li><b>:name</b> - The value is the exact name to use</li>
+ * <li><b>:name@ValueFrom</b> - The value is the name of another submitted parameter whose value is the exact name to use</li>
+ * <li><b>:nameHint</b> - The value is filtered, trimmed and made unique</li>
+ * <li><b>:nameHint@ValueFrom</b> - The value is the name of another submitted parameter whose value is filtered, trimmed and made unique</li>
+ * <li><b>otherwise</b> - Try the value of any server-side configured "principalNameHints" parameter to treat as a hint that is filtered, trimmed and made unique</li>
+ * </ul>
+ * </dd>
* <dt>*</dt>
* <dd>Any additional parameters become properties of the group node (optional)</dd>
* </dl>
@@ -118,6 +130,31 @@
super.deactivate();
}
+ @Reference(cardinality = ReferenceCardinality.MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC)
+ @Override
+ protected void bindPrincipalNameGenerator(PrincipalNameGenerator generator, Map<String, Object> properties) {
+ super.bindPrincipalNameGenerator(generator, properties);
+ }
+
+ @Override
+ protected void unbindPrincipalNameGenerator(PrincipalNameGenerator generator) { // NOSONAR
+ super.unbindPrincipalNameGenerator(generator);
+ }
+
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL,
+ policy = ReferencePolicy.DYNAMIC,
+ policyOption = ReferencePolicyOption.GREEDY)
+ @Override
+ protected void bindPrincipalNameFilter(PrincipalNameFilter filter) {
+ super.bindPrincipalNameFilter(filter);
+ }
+
+ @Override
+ protected void unbindPrincipalNameFilter(PrincipalNameFilter filter) { // NOSONAR
+ super.unbindPrincipalNameFilter(filter);
+ }
+
/* (non-Javadoc)
* @see org.apache.sling.jackrabbit.usermanager.impl.post.AbstractAuthorizablePostServlet#bindSystemUserManagerPaths(org.apache.sling.jackrabbit.usermanager.impl.resource.SystemUserManagerPaths)
*/
@@ -187,21 +224,28 @@
throw new IllegalArgumentException("JCR Session not found");
}
- if (name == null || name.length() == 0) {
+ final String principalName;
+ if (name == null || name.isEmpty()) {
+ principalName = getOrGeneratePrincipalName(jcrSession, properties, AuthorizableType.GROUP);
+ } else {
+ principalName = name;
+ }
+
+ if (principalName == null || principalName.length() == 0) {
throw new IllegalArgumentException("Group name was not supplied");
}
UserManager userManager = AccessControlUtil.getUserManager(jcrSession);
- Authorizable authorizable = userManager.getAuthorizable(name);
+ Authorizable authorizable = userManager.getAuthorizable(principalName);
Group group = null;
if (authorizable != null) {
// principal already exists!
throw new RepositoryException(
"A group already exists with the requested name: "
- + name);
+ + principalName);
} else {
- group = userManager.createGroup(() -> name);
+ group = userManager.createGroup(() -> principalName);
String groupPath = systemUserManagerPaths.getGroupPrefix()
+ group.getID();
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/CreateUserServlet.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/CreateUserServlet.java
index 89262c9..a06f0ae 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/CreateUserServlet.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/CreateUserServlet.java
@@ -30,10 +30,13 @@
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.AuthorizableType;
import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.jackrabbit.usermanager.CreateUser;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameFilter;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator;
import org.apache.sling.jackrabbit.usermanager.resource.SystemUserManagerPaths;
import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.jcr.base.util.AccessControlUtil;
@@ -50,6 +53,7 @@
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.component.annotations.ReferencePolicyOption;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@@ -72,8 +76,16 @@
* </ul>
* <h3>Post Parameters</h3>
* <dl>
- * <dt>:name</dt>
- * <dd>The name of the new user (required)</dd>
+ * <dt>one of these</dt>
+ * <dd>
+ * <ul>
+ * <li><b>:name</b> - The value is the exact name to use</li>
+ * <li><b>:name@ValueFrom</b> - The value is the name of another submitted parameter whose value is the exact name to use</li>
+ * <li><b>:nameHint</b> - The value is filtered, trimmed and made unique</li>
+ * <li><b>:nameHint@ValueFrom</b> - The value is the name of another submitted parameter whose value is filtered, trimmed and made unique</li>
+ * <li><b>otherwise</b> - Try the value of any server-side configured "principalNameHints" parameter to treat as a hint that is filtered, trimmed and made unique</li>
+ * </ul>
+ * </dd>
* <dt>:pwd</dt>
* <dd>The password of the new user (required)</dd>
* <dt>:pwdConfirm</dt>
@@ -182,9 +194,35 @@
@Override
@Deactivate
protected void deactivate() {
+ this.selfRegistrationEnabled = false;
super.deactivate();
}
+ @Reference(cardinality = ReferenceCardinality.MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC)
+ @Override
+ protected void bindPrincipalNameGenerator(PrincipalNameGenerator generator, Map<String, Object> properties) {
+ super.bindPrincipalNameGenerator(generator, properties);
+ }
+
+ @Override
+ protected void unbindPrincipalNameGenerator(PrincipalNameGenerator generator) { // NOSONAR
+ super.unbindPrincipalNameGenerator(generator);
+ }
+
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL,
+ policy = ReferencePolicy.DYNAMIC,
+ policyOption = ReferencePolicyOption.GREEDY)
+ @Override
+ protected void bindPrincipalNameFilter(PrincipalNameFilter filter) {
+ super.bindPrincipalNameFilter(filter);
+ }
+
+ @Override
+ protected void unbindPrincipalNameFilter(PrincipalNameFilter filter) { // NOSONAR
+ super.unbindPrincipalNameFilter(filter);
+ }
+
/* (non-Javadoc)
* @see org.apache.sling.jackrabbit.usermanager.impl.post.AbstractAuthorizablePostServlet#bindSystemUserManagerPaths(org.apache.sling.jackrabbit.usermanager.impl.resource.SystemUserManagerPaths)
*/
@@ -273,6 +311,13 @@
throw new RepositoryException("JCR Session not found");
}
+ final String principalName;
+ if (name == null || name.isEmpty()) {
+ principalName = getOrGeneratePrincipalName(jcrSession, properties, AuthorizableType.USER);
+ } else {
+ principalName = name;
+ }
+
// check for an administrator
boolean administrator = false;
try {
@@ -305,7 +350,7 @@
// check that the submitted parameter values have valid values.
- if (name == null || name.length() == 0) {
+ if (principalName == null || principalName.length() == 0) {
throw new RepositoryException("User name was not submitted");
}
if (password == null) {
@@ -328,15 +373,15 @@
}
UserManager userManager = AccessControlUtil.getUserManager(selfRegSession);
- Authorizable authorizable = userManager.getAuthorizable(name);
+ Authorizable authorizable = userManager.getAuthorizable(principalName);
if (authorizable != null) {
// user already exists!
throw new RepositoryException(
"A principal already exists with the requested name: "
- + name);
+ + principalName);
} else {
- user = userManager.createUser(name, password);
+ user = userManager.createUser(principalName, password);
String userPath = systemUserManagerPaths.getUserPrefix()
+ user.getID();
diff --git a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/PrincipalNameGeneratorImpl.java b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/PrincipalNameGeneratorImpl.java
new file mode 100644
index 0000000..35ba3da
--- /dev/null
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/post/PrincipalNameGeneratorImpl.java
@@ -0,0 +1,243 @@
+/*
+ * 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.post;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
+import org.apache.sling.api.request.RequestParameter;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameFilter;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator;
+import org.apache.sling.servlets.post.SlingPostConstants;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+/**
+ * Default implementation that generates a principal name based on a set of
+ * well-known request parameters
+ *
+ * <p>
+ * The value is resolved by the locating the first request parameter that is a
+ * match of one of the choices in the following order:
+ * <ol>
+ * <li>":name" - value is the exact name to use</li>
+ * <li>":name@ValueFrom" - value is the name of another submitted parameter whose value is the exact name to use</li>
+ * <li>":nameHint" - value is filtered, trimmed and made unique</li>
+ * <li>":nameHint@ValueFrom" - value is the name of another submitted parameter whose value is filtered, trimmed and made unique</li>
+ * <li>otherwise, try the value of any configured "principalNameHints" parameters to treat as a hint that is filtered, trimmed and made unique</li>
+ * </ol>
+ * </p>
+ */
+@Component(
+ configurationPid = "org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator",
+ service = {PrincipalNameGenerator.class})
+@Designate(ocd = PrincipalNameGeneratorImpl.Config.class)
+public class PrincipalNameGeneratorImpl implements PrincipalNameGenerator {
+
+ @ObjectClassDefinition(name = "Apache Sling Principal Name Generator",
+ description = "The Sling helper to generate a principal name from a hint")
+ public @interface Config {
+
+ @AttributeDefinition(name = "Maximum Principal Name Length",
+ description = "Maximum number of characters to "+
+ "use for automatically generated principal names. The default value is 20. Note, "+
+ "that actual principal names may be generated with at most 4 more characters if "+
+ "numeric suffixes must be appended to make the name unique.")
+ int principalNameMaxLength() default DEFAULT_MAX_NAME_LENGTH;
+
+ @AttributeDefinition(name = "Principal Name Hint Properties",
+ description = "The list of properties whose values "+
+ "may be used to derive a name for newly created principal. When handling a request "+
+ "to create a new principal, the name is automatically generated from this set if "+
+ "no \":name\" or \":nameHint\" property is provided. In this case the request "+
+ "parameters listed in this configuration value may be used as a hint to create the name.")
+ String[] principalNameHints();
+
+ }
+
+ private String[] parameterNames;
+
+ public static final int DEFAULT_MAX_NAME_LENGTH = 20;
+
+ private int maxLength = DEFAULT_MAX_NAME_LENGTH;
+
+ public PrincipalNameGeneratorImpl() {
+ this(null, -1);
+ }
+
+ public PrincipalNameGeneratorImpl(String[] parameterNames, int maxNameLength) {
+ if (parameterNames == null) {
+ this.parameterNames = new String[0];
+ } else {
+ this.parameterNames = parameterNames;
+ }
+
+ this.maxLength = (maxNameLength > 0)
+ ? maxNameLength
+ : DEFAULT_MAX_NAME_LENGTH;
+ }
+
+ @Activate
+ protected void activate(Config config) {
+ this.maxLength = config.principalNameMaxLength();
+ this.parameterNames = config.principalNameHints();
+ }
+
+ /**
+ * Convert the value to a list of strings
+ */
+ protected @NotNull List<String> valueToList(Object value) {
+ final List<String> valuesList;
+ if (value instanceof String[]) {
+ valuesList = Arrays.asList((String[])value);
+ } else if (value instanceof String) {
+ valuesList = Collections.singletonList((String)value);
+ } else if (value instanceof RequestParameter[]) {
+ valuesList = new ArrayList<>();
+ for (RequestParameter rp : (RequestParameter[])value) {
+ valuesList.add(rp.getString());
+ }
+ } else {
+ valuesList = Collections.emptyList();
+ }
+ return valuesList;
+ }
+
+ /**
+ * Determine the value to use for the specified parameter. This also
+ * considers the parameter with a {@link SlingPostConstants#VALUE_FROM_SUFFIX}
+ *
+ * @param parameters the map of request parameters
+ * @param paramName the parameter to get the value for
+ * @return the value to use for the parameter or null if it could not be determined
+ */
+ protected String getValueToUse(Map<String, ?> parameters, String paramName) {
+ String valueToUse = null;
+ List<String> values = valueToList(parameters.get(paramName));
+ if (!values.isEmpty()) {
+ for (String specialParam : values) {
+ if (specialParam != null && !specialParam.isEmpty()) {
+ valueToUse = specialParam;
+ }
+
+ if (valueToUse != null) {
+ if (valueToUse.isEmpty()) {
+ // empty value is not usable
+ valueToUse = null;
+ } else {
+ // found value, so stop looping
+ break;
+ }
+ }
+ }
+ } else {
+ // check for a paramName@ValueFrom param
+ // SLING-130: VALUE_FROM_SUFFIX means take the value of this
+ // property from a different field
+ values = valueToList(parameters.get(String.format("%s%s", paramName, SlingPostConstants.VALUE_FROM_SUFFIX)));
+ if (!values.isEmpty()) {
+ for (String specialParam : values) {
+ if (specialParam != null && !specialParam.isEmpty()) {
+ // retrieve the reference parameter value
+ List<String> refValues = valueToList(parameters.get(specialParam));
+ // @ValueFrom params must have exactly one value, else ignored
+ if (refValues.size() == 1) {
+ specialParam = refValues.get(0);
+ if (specialParam != null && !specialParam.isEmpty()) {
+ valueToUse = specialParam;
+ }
+ }
+ }
+
+ if (valueToUse != null) {
+ if (valueToUse.isEmpty()) {
+ // empty value is not usable
+ valueToUse = null;
+ } else {
+ // found value, so stop looping
+ break;
+ }
+ }
+ }
+ }
+ }
+ return valueToUse;
+ }
+
+ /**
+ * Get a "nice" principal name, if possible, based on given request
+ *
+ * @param request request
+ * @param type the type of principal
+ * @param defaultPrincipalNameGenerator the default principal name generator
+ *
+ * @return the principal name to be created or null if other PrincipalNameGenerators should be consulted
+ */
+ @Override
+ public NameInfo getPrincipalName(Map<String, ?> parameters, AuthorizableType type,
+ PrincipalNameFilter principalNameFilter, PrincipalNameGenerator defaultPrincipalNameGenerator) {
+ String valueToUse = null;
+ boolean doFilter = true;
+
+ // find the first request parameter that matches one of
+ // our parameterNames, in order, and has a value
+ // we first check for the special sling parameters
+ valueToUse = getValueToUse(parameters, SlingPostConstants.RP_NODE_NAME);
+ if (valueToUse != null) {
+ doFilter = false;
+ }
+ if ( valueToUse == null ) {
+ valueToUse = getValueToUse(parameters, SlingPostConstants.RP_NODE_NAME_HINT);
+
+ if (valueToUse == null && parameterNames != null) {
+ for (String param : parameterNames) {
+ valueToUse = getValueToUse(parameters, param);
+ if (valueToUse != null) {
+ break;
+ }
+ }
+ }
+ }
+
+ String result = valueToUse;
+ // should we filter?
+ if (doFilter && result != null && principalNameFilter != null) {
+ // filter value so that it works as a principal name
+ result = principalNameFilter.filter(result);
+ }
+
+ // max length
+ if (doFilter && result != null && result.length() > maxLength) {
+ result = result.substring(0, maxLength);
+ }
+
+ if (result != null) {
+ return new NameInfo(result, doFilter);
+ } else {
+ return null;
+ }
+ }
+
+}
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 a433e65..003bd56 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.
*/
-@org.osgi.annotation.versioning.Version("2.4.0")
+@org.osgi.annotation.versioning.Version("2.5.0")
package org.apache.sling.jackrabbit.usermanager;
diff --git a/src/test/java/org/apache/sling/jackrabbit/usermanager/impl/post/PrincipalNameGeneratorImplTest.java b/src/test/java/org/apache/sling/jackrabbit/usermanager/impl/post/PrincipalNameGeneratorImplTest.java
new file mode 100644
index 0000000..267a282
--- /dev/null
+++ b/src/test/java/org/apache/sling/jackrabbit/usermanager/impl/post/PrincipalNameGeneratorImplTest.java
@@ -0,0 +1,274 @@
+/*
+ * 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.post;
+
+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.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
+import org.apache.sling.api.request.RequestParameter;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameFilter;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator.NameInfo;
+import org.apache.sling.jackrabbit.usermanager.impl.post.AbstractAuthorizablePostServlet.RequestParameterImpl;
+import org.apache.sling.jcr.jackrabbit.usermanager.it.post.CustomPrincipalNameFilterImpl;
+import org.apache.sling.servlets.post.SlingPostConstants;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PrincipalNameGeneratorImplTest {
+
+ @Parameters(name = "Type: {0}")
+ public static Iterable<AuthorizableType> data() {
+ return Arrays.asList(AuthorizableType.USER, AuthorizableType.GROUP);
+ }
+
+ private PrincipalNameGenerator defaultGenerator = new PrincipalNameGeneratorImpl();
+ private AuthorizableType type;
+
+ public PrincipalNameGeneratorImplTest(AuthorizableType type) {
+ this.type = type;
+ }
+
+ @Test
+ public void testPrincipalNameFromName() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put(SlingPostConstants.RP_NODE_NAME, new RequestParameter[] {
+ new RequestParameterImpl("name1", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("name1", nameInfo.getPrincipalName());
+ assertFalse(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromNameHint() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put(SlingPostConstants.RP_NODE_NAME_HINT, new RequestParameter[] {
+ new RequestParameterImpl("name1", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("name1", nameInfo.getPrincipalName());
+ assertTrue(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromNameHintWithFilter() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = new CustomPrincipalNameFilterImpl();
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put(SlingPostConstants.RP_NODE_NAME_HINT, new RequestParameter[] {
+ new RequestParameterImpl("Na me1", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("na_me1", nameInfo.getPrincipalName());
+ assertTrue(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromNameHintTooLong() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put(SlingPostConstants.RP_NODE_NAME_HINT, new RequestParameter[] {
+ new RequestParameterImpl("namethatistoolong123456789", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("namethatistoolong123", nameInfo.getPrincipalName());
+ assertTrue(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromNameValueFrom() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put("displayName", new RequestParameter[] {
+ new RequestParameterImpl("name1", "UTF-8")
+ });
+ parameters.put(String.format("%s%s", SlingPostConstants.RP_NODE_NAME, SlingPostConstants.VALUE_FROM_SUFFIX), new RequestParameter[] {
+ new RequestParameterImpl("displayName", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("name1", nameInfo.getPrincipalName());
+ assertFalse(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromNameValueFromTooLong() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put("displayName", new RequestParameter[] {
+ new RequestParameterImpl("namethatistoolong123456789", "UTF-8")
+ });
+ parameters.put(String.format("%s%s", SlingPostConstants.RP_NODE_NAME, SlingPostConstants.VALUE_FROM_SUFFIX), new RequestParameter[] {
+ new RequestParameterImpl("displayName", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("namethatistoolong123456789", nameInfo.getPrincipalName());
+ assertFalse(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromNameValueFromWithFilter() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = new CustomPrincipalNameFilterImpl();
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put("displayName", new RequestParameter[] {
+ new RequestParameterImpl("Na me1", "UTF-8")
+ });
+ parameters.put(String.format("%s%s", SlingPostConstants.RP_NODE_NAME, SlingPostConstants.VALUE_FROM_SUFFIX), new RequestParameter[] {
+ new RequestParameterImpl("displayName", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("Na me1", nameInfo.getPrincipalName());
+ assertFalse(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromNameHintValueFrom() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put("displayName", new RequestParameter[] {
+ new RequestParameterImpl("name1", "UTF-8")
+ });
+ parameters.put(String.format("%s%s", SlingPostConstants.RP_NODE_NAME_HINT, SlingPostConstants.VALUE_FROM_SUFFIX), new RequestParameter[] {
+ new RequestParameterImpl("displayName", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("name1", nameInfo.getPrincipalName());
+ assertTrue(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromNameHintValueFromTooLong() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put("displayName", new RequestParameter[] {
+ new RequestParameterImpl("namethatistoolong123456789", "UTF-8")
+ });
+ parameters.put(String.format("%s%s", SlingPostConstants.RP_NODE_NAME_HINT, SlingPostConstants.VALUE_FROM_SUFFIX), new RequestParameter[] {
+ new RequestParameterImpl("displayName", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("namethatistoolong123", nameInfo.getPrincipalName());
+ assertTrue(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromNameHintValueFromWithFilter() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = new CustomPrincipalNameFilterImpl();
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put("displayName", new RequestParameter[] {
+ new RequestParameterImpl("Na me1", "UTF-8")
+ });
+ parameters.put(String.format("%s%s", SlingPostConstants.RP_NODE_NAME_HINT, SlingPostConstants.VALUE_FROM_SUFFIX), new RequestParameter[] {
+ new RequestParameterImpl("displayName", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("na_me1", nameInfo.getPrincipalName());
+ assertTrue(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromConfiguredHint() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl(new String[] {
+ "displayName2"
+ },
+ 10);
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put("displayName2", new RequestParameter[] {
+ new RequestParameterImpl("name1", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("name1", nameInfo.getPrincipalName());
+ assertTrue(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromConfiguredHintWithFilter() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl(new String[] {
+ "displayName2"
+ },
+ 10);
+ PrincipalNameFilter filter = new CustomPrincipalNameFilterImpl();
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put("displayName2", new RequestParameter[] {
+ new RequestParameterImpl("Na me1", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("na_me1", nameInfo.getPrincipalName());
+ assertTrue(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameFromConfiguredHintTooLong() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl(new String[] {
+ "displayName2"
+ },
+ 10);
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ parameters.put("displayName2", new RequestParameter[] {
+ new RequestParameterImpl("namethatistoolong", "UTF-8")
+ });
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNotNull(nameInfo);
+ assertEquals("namethatis", nameInfo.getPrincipalName());
+ assertTrue(nameInfo.isMakeUnique());
+ }
+
+ @Test
+ public void testPrincipalNameNotFound() {
+ PrincipalNameGenerator generator = new PrincipalNameGeneratorImpl();
+ PrincipalNameFilter filter = null;
+ Map<String, RequestParameter[]> parameters = new HashMap<>();
+ NameInfo nameInfo = generator.getPrincipalName(parameters, type, filter, defaultGenerator);
+ assertNull(nameInfo);
+ }
+}
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 b2996ad..4c58008 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
@@ -129,10 +129,10 @@
// may remove at a later date if the superclass includes these versions or later
versionResolver.setVersionFromProject("org.apache.sling", "org.apache.sling.api");
versionResolver.setVersion("org.apache.sling", "org.apache.sling.engine", "2.7.10"); // to be compatible with current o.a.sling.api
- versionResolver.setVersion("org.apache.sling", "org.apache.sling.resourceresolver", "1.7.10"); // to be compatible with current o.a.sling.api
+ versionResolver.setVersion("org.apache.sling", "org.apache.sling.resourceresolver", "1.8.0"); // to be compatible with current o.a.sling.api
versionResolver.setVersion("org.apache.sling", "org.apache.sling.scripting.core", "2.4.0"); // to be compatible with current o.a.sling.api
versionResolver.setVersion("org.apache.sling", "org.apache.sling.scripting.api", "2.2.0"); // to be compatible with current o.a.sling.api
- versionResolver.setVersion("org.apache.sling", "org.apache.sling.servlets.resolver", "2.8.3-SNAPSHOT"); // to be compatible with current o.a.sling.api
+ versionResolver.setVersion("org.apache.sling", "org.apache.sling.servlets.resolver", "2.9.0"); // to be compatible with current o.a.sling.api
versionResolver.setVersion("org.apache.sling", "org.apache.sling.commons.compiler", "2.4.0"); // to be compatible with current o.a.sling.scripting.core
return composite(
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreateGroupIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreateGroupIT.java
index 3c06703..3f172ac 100644
--- a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreateGroupIT.java
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreateGroupIT.java
@@ -17,7 +17,9 @@
package org.apache.sling.jcr.jackrabbit.usermanager.it.post;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
@@ -31,6 +33,7 @@
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.message.BasicNameValuePair;
+import org.apache.sling.api.resource.ResourceUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.PaxExam;
@@ -207,4 +210,262 @@
testCreateGroupRedirect("https://", SC_UNPROCESSABLE_ENTITY);
}
+ /**
+ * SLING-10902 Test for group name that is not unique
+ */
+ @Test
+ public void testCreateGroupWithAlreadyUsedName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertEquals(marker, testGroupId);
+
+ // second time with the same info fails since it is not unique
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * SLING-10902 Test for group name that is not unique
+ */
+ @Test
+ public void testCreateGroupWithAlreadyUsedNameValueFrom() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name@ValueFrom", "marker"));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertEquals(marker, testGroupId);
+
+ // second time with the same info fails since it is not unique
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * SLING-10902 Test for group name generated from a hint
+ */
+ @Test
+ public void testCreateGroupWithNameHintAndAlreadyUsedName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String hint = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", hint));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertEquals(hint.substring(0, 20), testGroupId);
+
+ // second time with the same info generates a different unique name
+ json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId2 = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId2);
+ assertTrue(testGroupId2.startsWith(hint.substring(0, 20)));
+ assertNotEquals(testGroupId, testGroupId2);
+ }
+
+
+ /**
+ * SLING-10902 Test for group name generated from the value of another param
+ */
+ @Test
+ public void testCreateGroupWithNameValueFrom() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name@ValueFrom", "marker"));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertEquals(marker, testGroupId);
+ }
+
+ /**
+ * SLING-10902 Test for group name generated from a hint
+ */
+ @Test
+ public void testCreateGroupWithNameHint() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String hint = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", hint));
+ postParams.add(new BasicNameValuePair("marker", testUserId));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertEquals(hint.substring(0, 20), testGroupId);
+ }
+
+ /**
+ * SLING-10902 Test for group name generated from a hint value of another param
+ */
+ @Test
+ public void testCreateGroupWithNameHintValueFrom() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint@ValueFrom", "marker"));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertEquals(marker.substring(0, 20), testGroupId);
+ }
+
+ /**
+ * SLING-10902 Test for group name generated without a hint
+ */
+ @Test
+ public void testCreateGroupWithNoName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * SLING-10902 Test for group name generated without a hint
+ */
+ @Test
+ public void testCreateGroupWithEmptyName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", ""));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * SLING-10902 Test for group name generated without a hint
+ */
+ @Test
+ public void testCreateGroupWithEmptyNameHint() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", ""));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+
+ /**
+ * SLING-10902 Test for group name generated from a default property name
+ */
+ @Test
+ public void testCreateGroupWithNoNameAndAlternateHintProp() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("displayName", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertEquals(marker.substring(0, 20), testGroupId);
+ }
+
+ /**
+ * SLING-10902 Test for group name generated from a default property name
+ */
+ @Test
+ public void testCreateGroupWithEmptyNameAndAlternateHintProp() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", ""));
+ postParams.add(new BasicNameValuePair("displayName", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertEquals(marker.substring(0, 20), testGroupId);
+ }
+
+ /**
+ * SLING-10902 Test for group name generated from a default property name
+ */
+ @Test
+ public void testCreateGroupWithEmptyNameHintAndAlternateHintProp() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", ""));
+ postParams.add(new BasicNameValuePair("displayName", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertEquals(marker.substring(0, 20), testGroupId);
+ }
+
}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreatePrincipalWithCustomNameGeneratorIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreatePrincipalWithCustomNameGeneratorIT.java
new file mode 100644
index 0000000..a5565b8
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreatePrincipalWithCustomNameGeneratorIT.java
@@ -0,0 +1,324 @@
+/*
+ * 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.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameFilter;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator;
+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;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Tests for the 'createUser' and 'createGroup' Sling Post Operation
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class CreatePrincipalWithCustomNameGeneratorIT extends UserManagerClientTestSupport {
+
+ protected ServiceRegistration<PrincipalNameGenerator> principalNameGeneratorServiceReg;
+ protected ServiceRegistration<PrincipalNameFilter> principalNameFilterServiceReg;
+
+ @Override
+ public void before() throws IOException, URISyntaxException {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ Dictionary<String, Object> props = new Hashtable<>(); // NOSONAR
+ props.put(Constants.SERVICE_RANKING, 1);
+ principalNameGeneratorServiceReg = bundle.getBundleContext().registerService(PrincipalNameGenerator.class,
+ new CustomPrincipalNameGeneratorImpl(), props);
+
+ Dictionary<String, Object> props2 = new Hashtable<>(); // NOSONAR
+ props2.put(Constants.SERVICE_RANKING, 1);
+ principalNameFilterServiceReg = bundle.getBundleContext().registerService(PrincipalNameFilter.class,
+ new CustomPrincipalNameFilterImpl(), props2);
+
+ super.before();
+ }
+
+ @Override
+ public void after() throws IOException {
+ if (principalNameGeneratorServiceReg != null) {
+ principalNameGeneratorServiceReg.unregister();
+ principalNameGeneratorServiceReg = null;
+ }
+ if (principalNameFilterServiceReg != null) {
+ principalNameFilterServiceReg.unregister();
+ principalNameFilterServiceReg = null;
+ }
+
+ super.after();
+ }
+
+ /**
+ * Test for user name generated from a hint
+ */
+ @Test
+ public void testCreateUserWithNameHint() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String hint = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", hint));
+ postParams.add(new BasicNameValuePair("marker", testUserId));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertTrue(testUserId.startsWith("custom_user_"));
+ }
+
+ /**
+ * Test for user name generated from a hint value of another param
+ */
+ @Test
+ public void testCreateUserWithNameHintValueFrom() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint@ValueFrom", "marker"));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertTrue(testUserId.startsWith("custom_user_"));
+ }
+
+ /**
+ * Test for user name generated without a hint
+ */
+ @Test
+ public void testCreateUserWithNoName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("marker", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * Test for user name generated without a hint
+ */
+ @Test
+ public void testCreateUserWithEmptyName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", ""));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * Test for user name generated without a hint
+ */
+ @Test
+ public void testCreateUserWithEmptyNameHint() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", ""));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * Test for user name generated from a default property name
+ */
+ @Test
+ public void testCreateUserWithHintFromDefaultPropertyName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("marker", marker));
+ postParams.add(new BasicNameValuePair("displayName", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertTrue(testUserId.startsWith("custom_user_"));
+ }
+
+
+
+ /**
+ * Test for group name generated from a hint
+ */
+ @Test
+ public void testCreateGroupWithNameHint() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String hint = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", hint));
+ postParams.add(new BasicNameValuePair("marker", testUserId));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertTrue(testGroupId.startsWith("custom_group_"));
+ }
+
+ /**
+ * Test for group name generated from a hint value of another param
+ */
+ @Test
+ public void testCreateGroupWithNameHintValueFrom() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint@ValueFrom", "marker"));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertTrue(testGroupId.startsWith("custom_group_"));
+ }
+
+ /**
+ * Test for group name generated without a hint
+ */
+ @Test
+ public void testCreateGroupWithNoName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * Test for group name generated without a hint
+ */
+ @Test
+ public void testCreateGroupWithEmptyName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", ""));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * Test for group name generated without a hint
+ */
+ @Test
+ public void testCreateGroupWithEmptyNameHint() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", ""));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * Test for group name generated from a default property name
+ */
+ @Test
+ public void testCreateGroupWithHintFromDefaultPropertyName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/group.create.json", baseServerUri);
+
+ String marker = "testGroup" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("marker", marker));
+ postParams.add(new BasicNameValuePair("displayName", marker));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testGroupId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testGroupId);
+ assertTrue(testGroupId.startsWith("custom_group_"));
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreateUserIT.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreateUserIT.java
index acf8e6e..a049ee3 100644
--- a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreateUserIT.java
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CreateUserIT.java
@@ -18,7 +18,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
@@ -32,6 +34,7 @@
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.message.BasicNameValuePair;
+import org.apache.sling.api.resource.ResourceUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.PaxExam;
@@ -303,4 +306,284 @@
testCreateUserRedirect("https://", SC_UNPROCESSABLE_ENTITY);
}
+ /**
+ * SLING-10902 Test for user name that is not unique
+ */
+ @Test
+ public void testCreateUserWithAlreadyUsedName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertEquals(marker, testUserId);
+
+ // second time with the same info fails since it is not unique
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * SLING-10902 Test for user name that is not unique
+ */
+ @Test
+ public void testCreateUserWithAlreadyUsedNameValueFrom() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name@ValueFrom", "marker"));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertEquals(marker, testUserId);
+
+ // second time with the same info fails since it is not unique
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * SLING-10902 Test for user name generated from a hint
+ */
+ @Test
+ public void testCreateUserWithNameHintAndAlreadyUsedName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String hint = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", hint));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertEquals(hint.substring(0, 20), testUserId);
+
+ // second time with the same info generates a different unique name
+ json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId2 = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId2);
+ assertTrue(testUserId2.startsWith(hint.substring(0, 20)));
+ assertNotEquals(testUserId, testUserId2);
+ }
+
+
+ /**
+ * SLING-10902 Test for user name generated from the value of another param
+ */
+ @Test
+ public void testCreateUserWithNameValueFrom() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name@ValueFrom", "marker"));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertEquals(marker, testUserId);
+ }
+
+ /**
+ * SLING-10902 Test for user name generated from a hint
+ */
+ @Test
+ public void testCreateUserWithNameHint() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String hint = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", hint));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertEquals(hint.substring(0, 20), testUserId);
+ }
+
+ /**
+ * SLING-10902 Test for user name generated from a hint value of another param
+ */
+ @Test
+ public void testCreateUserWithNameHintValueFrom() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint@ValueFrom", "marker"));
+ postParams.add(new BasicNameValuePair("marker", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertEquals(marker.substring(0, 20), testUserId);
+ }
+
+ /**
+ * SLING-10902 Test for user name generated without a hint but one of the alternate name hint
+ * properties is supplied
+ */
+ @Test
+ public void testCreateUserWithNoName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * SLING-10902 Test for user name generated without a hint but one of the alternate name hint
+ * properties is supplied
+ */
+ @Test
+ public void testCreateUserWithEmptyName() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", ""));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * SLING-10902 Test for user name generated without a hint but one of the alternate name hint
+ * properties is supplied
+ */
+ @Test
+ public void testCreateUserWithEmptyNameHint() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", ""));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * SLING-10902 Test for user name generated without a hint but one of the alternate name hint
+ * properties is supplied
+ */
+ @Test
+ public void testCreateUserWithNoNameAndAlternateHintProp() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair("displayName", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertEquals(marker.substring(0, 20), testUserId);
+ }
+
+ /**
+ * SLING-10902 Test for user name generated without a hint but one of the alternate name hint
+ * properties is supplied
+ */
+ @Test
+ public void testCreateUserWithEmptyNameAndAlternateHintProp() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":name", ""));
+ postParams.add(new BasicNameValuePair("displayName", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertEquals(marker.substring(0, 20), testUserId);
+ }
+
+ /**
+ * SLING-10902 Test for user name generated without a hint but one of the alternate name hint
+ * properties is supplied
+ */
+ @Test
+ public void testCreateUserWithEmptyNameHintAndAlternateHintProp() throws IOException, JsonException {
+ String postUrl = String.format("%s/system/userManager/user.create.json", baseServerUri);
+
+ String marker = "testUser" + getNextInt();
+ List<NameValuePair> postParams = new ArrayList<>();
+ postParams.add(new BasicNameValuePair(":nameHint", ""));
+ postParams.add(new BasicNameValuePair("displayName", marker));
+ postParams.add(new BasicNameValuePair("pwd", "testPwd"));
+ postParams.add(new BasicNameValuePair("pwdConfirm", "testPwd"));
+ Credentials creds = new UsernamePasswordCredentials("admin", "admin");
+ String json = getAuthenticatedPostContent(creds, postUrl, CONTENT_TYPE_JSON, postParams, HttpServletResponse.SC_OK);
+
+ //make sure the json response can be parsed as a JSON object
+ JsonObject jsonObj = parseJson(json);
+ assertNotNull(jsonObj);
+ testUserId = ResourceUtil.getName(jsonObj.getString("path"));
+ assertNotNull(testUserId);
+ assertEquals(marker.substring(0, 20), testUserId);
+ }
+
}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CustomPrincipalNameFilterImpl.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CustomPrincipalNameFilterImpl.java
new file mode 100644
index 0000000..61e0a0c
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CustomPrincipalNameFilterImpl.java
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.sling.jackrabbit.usermanager.PrincipalNameFilter;
+
+/**
+ * Filter a String so that it can be used as a principal name.
+ */
+public class CustomPrincipalNameFilterImpl implements PrincipalNameFilter {
+
+ public static final String ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_.-";
+ public static final char REPLACEMENT_CHAR = '_';
+
+ @Override
+ public String filter(String principalName) {
+ final StringBuilder sb = new StringBuilder();
+ char lastAdded = 0;
+
+ principalName = principalName.toLowerCase();
+ for(int i=0; i < principalName.length(); i++) {
+ final char c = principalName.charAt(i);
+ char toAdd = c;
+
+ if (ALLOWED_CHARS.indexOf(c) < 0) {
+ if (lastAdded == REPLACEMENT_CHAR) {
+ // do not add several _ in a row
+ continue;
+ }
+ toAdd = REPLACEMENT_CHAR;
+
+ } else if(i == 0 && Character.isDigit(c)) {
+ sb.append(REPLACEMENT_CHAR);
+ }
+
+ sb.append(toAdd);
+ lastAdded = toAdd;
+ }
+
+ if (sb.length()==0) {
+ sb.append(REPLACEMENT_CHAR);
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CustomPrincipalNameGeneratorImpl.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CustomPrincipalNameGeneratorImpl.java
new file mode 100644
index 0000000..62a5f52
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/CustomPrincipalNameGeneratorImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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 java.util.Map;
+
+import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameFilter;
+import org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator;
+
+/**
+ * Sample implementation of the PrincipalNameGenerator interface.
+ */
+public class CustomPrincipalNameGeneratorImpl implements PrincipalNameGenerator {
+
+ @Override
+ public NameInfo getPrincipalName(Map<String, ?> parameters, AuthorizableType type,
+ PrincipalNameFilter principalNameFilter, PrincipalNameGenerator defaultPrincipalNameGenerator) {
+ NameInfo nameInfo = defaultPrincipalNameGenerator.getPrincipalName(parameters, type,
+ principalNameFilter, defaultPrincipalNameGenerator);
+ if (nameInfo != null && nameInfo.getPrincipalName() != null && nameInfo.isMakeUnique()) {
+ String principalName = String.format("custom_%s_%s", type.name().toLowerCase(), nameInfo.getPrincipalName());
+ nameInfo = new NameInfo(principalName, nameInfo.isMakeUnique());
+ }
+ return nameInfo;
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/UserManagerClientTestSupport.java b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/UserManagerClientTestSupport.java
index 64c96cc..48b23e3 100644
--- a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/UserManagerClientTestSupport.java
+++ b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/UserManagerClientTestSupport.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.fail;
import static org.ops4j.pax.exam.CoreOptions.when;
import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
import java.io.IOException;
import java.io.StringReader;
@@ -118,6 +119,7 @@
protected String testUserId2 = null;
protected String testUserId3 = null;
protected String testGroupId = null;
+ protected String testGroupId2 = null;
protected String testFolderUrl = null;
@Override
@@ -157,6 +159,10 @@
factoryConfiguration("org.apache.sling.jcr.contentloader.hc.BundleContentLoadedCheck")
.put("hc.tags", new String[] {"bundles"})
.asOption(),
+ // SLING-10902 configure principal name hint
+ newConfiguration("org.apache.sling.jackrabbit.usermanager.PrincipalNameGenerator")
+ .put("principalNameHints", new String[] {"displayName"})
+ .asOption()
};
}
@@ -209,6 +215,11 @@
String postUrl = String.format("%s/system/userManager/group/%s.delete.html", baseServerUri, testGroupId);
assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, Collections.emptyList(), null);
}
+ if (testGroupId2 != null) {
+ //remove the test user if it exists.
+ String postUrl = String.format("%s/system/userManager/group/%s.delete.html", baseServerUri, testGroupId2);
+ assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, Collections.emptyList(), null);
+ }
if (testUserId != null) {
//remove the test user if it exists.
String postUrl = String.format("%s/system/userManager/user/%s.delete.html", baseServerUri, testUserId);