keycloak-uncached
Changes
federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java 29(+29 -0)
federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java 60(+60 -0)
Details
diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPDn.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPDn.java
index 8bfbf6d..94014fa 100644
--- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPDn.java
+++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPDn.java
@@ -27,7 +27,15 @@ import java.util.LinkedList;
*/
public class LDAPDn {
- private final Deque<Entry> entries = new LinkedList<>();
+ private final Deque<Entry> entries;
+
+ private LDAPDn() {
+ this.entries = new LinkedList<>();
+ }
+
+ private LDAPDn(Deque<Entry> entries) {
+ this.entries = entries;
+ }
public static LDAPDn fromString(String dnString) {
LDAPDn dn = new LDAPDn();
@@ -115,12 +123,14 @@ public class LDAPDn {
/**
*
- * @return string like "dc=something,dc=org" from the DN like "uid=joe,dc=something,dc=org"
+ * @return DN like "dc=something,dc=org" from the DN like "uid=joe,dc=something,dc=org".
+ * Returned DN will be new clone not related to the original DN instance.
+ *
*/
- public String getParentDn() {
+ public LDAPDn getParentDn() {
LinkedList<Entry> parentDnEntries = new LinkedList<>(entries);
parentDnEntries.remove();
- return toString(parentDnEntries);
+ return new LDAPDn(parentDnEntries);
}
public boolean isDescendantOf(LDAPDn expectedParentDn) {
diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java
index c0e84b0..249a3a0 100644
--- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java
+++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java
@@ -103,6 +103,8 @@ public class LDAPIdentityStore implements IdentityStore {
@Override
public void update(LDAPObject ldapObject) {
+ checkRename(ldapObject);
+
BasicAttributes updatedAttributes = extractAttributes(ldapObject, false);
NamingEnumeration<Attribute> attributes = updatedAttributes.getAll();
@@ -114,6 +116,33 @@ public class LDAPIdentityStore implements IdentityStore {
}
}
+ protected void checkRename(LDAPObject ldapObject) {
+ String rdnAttrName = ldapObject.getRdnAttributeName();
+ if (ldapObject.getReadOnlyAttributeNames().contains(rdnAttrName.toLowerCase())) {
+ return;
+ }
+
+ String rdnAttrVal = ldapObject.getAttributeAsString(rdnAttrName);
+
+ String oldRdnAttrVal = ldapObject.getDn().getFirstRdnAttrValue();
+ if (!oldRdnAttrVal.equals(rdnAttrVal)) {
+ LDAPDn newLdapDn = ldapObject.getDn().getParentDn();
+ newLdapDn.addFirst(rdnAttrName, rdnAttrVal);
+
+ String oldDn = ldapObject.getDn().toString();
+ String newDn = newLdapDn.toString();
+
+ if (logger.isDebugEnabled()) {
+ logger.debugf("Renaming LDAP Object. Old DN: [%s], New DN: [%s]", oldDn, newDn);
+ }
+
+ // In case, that there is conflict (For example already existing "CN=John Anthony"), the different DN is returned
+ newDn = this.operationManager.renameEntry(oldDn, newDn, true);
+
+ ldapObject.setDn(LDAPDn.fromString(newDn));
+ }
+ }
+
@Override
public void remove(LDAPObject ldapObject) {
this.operationManager.removeEntry(ldapObject.getDn().toString());
diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java
index 350b16d..f057fc8 100644
--- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java
+++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java
@@ -21,6 +21,7 @@ import org.jboss.logging.Logger;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
import org.keycloak.storage.ldap.LDAPConfig;
+import org.keycloak.storage.ldap.idm.model.LDAPDn;
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
import org.keycloak.storage.ldap.mappers.LDAPOperationDecorator;
@@ -28,6 +29,7 @@ import javax.naming.AuthenticationException;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
+import javax.naming.NameAlreadyBoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
@@ -158,6 +160,64 @@ public class LDAPOperationManager {
}
}
+
+ /**
+ * Rename LDAPObject name (DN)
+ *
+ * @param oldDn
+ * @param newDn
+ * @param fallback With fallback=true, we will try to find the another DN in case of conflict. For example if there is an
+ * attempt to rename to "CN=John Doe", but there is already existing "CN=John Doe", we will try "CN=John Doe0"
+ * @return the non-conflicting DN, which was used in the end
+ */
+ public String renameEntry(String oldDn, String newDn, boolean fallback) {
+ try {
+ String newNonConflictingDn = execute(new LdapOperation<String>() {
+ @Override
+ public String execute(LdapContext context) throws NamingException {
+ String dn = newDn;
+
+ // Max 5 attempts for now
+ int max = 5;
+ for (int i=0 ; i<max ; i++) {
+ try {
+ context.rename(oldDn, dn);
+ return dn;
+ } catch (NameAlreadyBoundException ex) {
+ if (!fallback) {
+ throw ex;
+ } else {
+ String failedDn = dn;
+ if (i<max) {
+ dn = findNextDNForFallback(newDn, i);
+ logger.warnf("Failed to rename DN [%s] to [%s]. Will try to fallback to DN [%s]", oldDn, failedDn, dn);
+ } else {
+ logger.warnf("Failed all fallbacks for renaming [%s]", oldDn);
+ throw ex;
+ }
+ }
+ }
+ }
+
+ throw new ModelException("Could not rename entry from DN [" + oldDn + "] to new DN [" + newDn + "]. All fallbacks failed");
+ }
+ });
+ return newNonConflictingDn;
+ } catch (NamingException e) {
+ throw new ModelException("Could not rename entry from DN [" + oldDn + "] to new DN [" + newDn + "]", e);
+ }
+ }
+
+ private String findNextDNForFallback(String newDn, int counter) {
+ LDAPDn dn = LDAPDn.fromString(newDn);
+ String rdnAttrName = dn.getFirstRdnAttrName();
+ String rdnAttrVal = dn.getFirstRdnAttrValue();
+ LDAPDn parentDn = dn.getParentDn();
+ parentDn.addFirst(rdnAttrName, rdnAttrVal + counter);
+ return parentDn.toString();
+ }
+
+
public List<SearchResult> search(final String baseDN, final String filter, Collection<String> returningAttributes, int searchScope) throws NamingException {
final List<SearchResult> result = new ArrayList<SearchResult>();
final SearchControls cons = getSearchControls(returningAttributes, searchScope);
diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java
index 43d631e..223a115 100644
--- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java
@@ -75,7 +75,7 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
@Override
public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser, RealmModel realm) {
String ldapFullNameAttrName = getLdapFullNameAttrName();
- String fullName = getFullName(localUser.getFirstName(), localUser.getLastName());
+ String fullName = getFullNameForWriteToLDAP(localUser.getFirstName(), localUser.getLastName(), localUser.getUsername());
ldapUser.setSingleAttribute(ldapFullNameAttrName, fullName);
if (isReadOnly()) {
@@ -103,7 +103,7 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
}
private void setFullNameToLDAPObject() {
- String fullName = getFullName(getFirstName(), getLastName());
+ String fullName = getFullNameForWriteToLDAP(getFirstName(), getLastName(), getUsername());
if (logger.isTraceEnabled()) {
logger.tracef("Pushing full name attribute to LDAP. Full name: %s", fullName);
}
@@ -177,18 +177,24 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
return ldapFullNameAttrName == null ? LDAPConstants.CN : ldapFullNameAttrName;
}
- protected String getFullName(String firstName, String lastName) {
- if (firstName != null && lastName != null) {
+ protected String getFullNameForWriteToLDAP(String firstName, String lastName, String username) {
+ if (!isBlank(firstName) && !isBlank(lastName)) {
return firstName + " " + lastName;
- } else if (firstName != null) {
+ } else if (!isBlank(firstName)) {
return firstName;
- } else if (lastName != null) {
+ } else if (!isBlank(lastName)) {
return lastName;
+ } else if (isFallbackToUsername()) {
+ return username;
} else {
return LDAPConstants.EMPTY_ATTRIBUTE_VALUE;
}
}
+ private boolean isBlank(String attr) {
+ return attr == null || attr.trim().isEmpty();
+ }
+
private boolean isReadOnly() {
return parseBooleanParameter(mapperModel, READ_ONLY);
}
@@ -196,4 +202,11 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
private boolean isWriteOnly() {
return parseBooleanParameter(mapperModel, WRITE_ONLY);
}
+
+
+ // Used just in case that we have Writable LDAP and fullName is mapped to "cn", which is used as RDN (this typically happens only on MSAD)
+ private boolean isFallbackToUsername() {
+ String rdnLdapAttrConfig = getLdapProvider().getLdapIdentityStore().getConfig().getRdnLdapAttribute();
+ return !isReadOnly() && getLdapFullNameAttrName().equalsIgnoreCase(rdnLdapAttrConfig);
+ }
}
diff --git a/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java b/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java
index a668cd7..d749c13 100644
--- a/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java
+++ b/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java
@@ -35,7 +35,7 @@ public class LDAPDnTest {
Assert.assertEquals("uid=Johny\\,Depp\\+Pepp\\\\Foo,ou=People,dc=keycloak,dc=org", dn.toString());
Assert.assertEquals(LDAPDn.fromString("uid=Johny\\,Depp\\+Pepp\\\\Foo,ou=People,dc=keycloak,dc=org"), dn);
- Assert.assertEquals("ou=People,dc=keycloak,dc=org", dn.getParentDn());
+ Assert.assertEquals("ou=People,dc=keycloak,dc=org", dn.getParentDn().toString());
Assert.assertTrue(dn.isDescendantOf(LDAPDn.fromString("dc=keycloak, dc=org")));
Assert.assertTrue(dn.isDescendantOf(LDAPDn.fromString("dc=org")));
@@ -46,4 +46,22 @@ public class LDAPDnTest {
Assert.assertEquals("uid", dn.getFirstRdnAttrName());
Assert.assertEquals("Johny,Depp+Pepp\\Foo", dn.getFirstRdnAttrValue());
}
+
+ @Test
+ public void testCorrectEscape() throws Exception {
+ LDAPDn dn = LDAPDn.fromString("dc=keycloak, dc=org");
+ dn.addFirst("cn", "Johny,Džýa Foo");
+ Assert.assertEquals("cn=Johny\\,Džýa Foo,dc=keycloak,dc=org", dn.toString());
+ Assert.assertEquals("Johny,Džýa Foo", dn.getFirstRdnAttrValue());
+
+ dn = LDAPDn.fromString("dc=keycloak, dc=org");
+ dn.addFirst("cn", "Johny,Džýa Foo ");
+ Assert.assertEquals("cn=Johny\\,Džýa Foo\\ ,dc=keycloak,dc=org", dn.toString());
+ Assert.assertEquals("Johny,Džýa Foo ", dn.getFirstRdnAttrValue());
+
+ dn = LDAPDn.fromString("dc=keycloak, dc=org");
+ dn.addFirst("cn", "Johny,Džýa ");
+ Assert.assertEquals("cn=Johny\\,Džýa\\ ,dc=keycloak,dc=org", dn.toString());
+ Assert.assertEquals("Johny,Džýa ", dn.getFirstRdnAttrValue());
+ }
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/MSADFullNameTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/MSADFullNameTest.java
new file mode 100644
index 0000000..fe6510d
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/MSADFullNameTest.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed 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.keycloak.testsuite.federation.storage.ldap;
+
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runners.MethodSorters;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.UserStorageProviderModel;
+import org.keycloak.storage.ldap.LDAPStorageProvider;
+import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
+import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapper;
+import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapperFactory;
+import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.pages.AccountPasswordPage;
+import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.RegisterPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.LDAPRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * Test for the MSAD setup with usernameAttribute=sAMAccountName, rdnAttribute=cn and fullNameMapper mapped to cn
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class MSADFullNameTest {
+
+ // Run this test just on MSAD and just when sAMAccountName is mapped to username
+ private static LDAPRule ldapRule = new LDAPRule((Map<String, String> ldapConfig) -> {
+
+ String vendor = ldapConfig.get(LDAPConstants.VENDOR);
+ if (!vendor.equals(LDAPConstants.VENDOR_ACTIVE_DIRECTORY)) {
+ return true;
+ }
+
+ String usernameAttr = ldapConfig.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
+ return !usernameAttr.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME);
+
+ });
+
+ private static ComponentModel ldapModel = null;
+
+ private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ LDAPTestUtils.addLocalUser(manager.getSession(), appRealm, "marykeycloak", "mary@test.com", "password-app");
+
+ MultivaluedHashMap<String,String> ldapConfig = LDAPTestUtils.getLdapRuleConfig(ldapRule);
+ ldapConfig.putSingle(LDAPConstants.SYNC_REGISTRATIONS, "true");
+ ldapConfig.putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.WRITABLE.toString());
+ UserStorageProviderModel model = new UserStorageProviderModel();
+ model.setLastSync(0);
+ model.setChangedSyncPeriod(-1);
+ model.setFullSyncPeriod(-1);
+ model.setName("test-ldap");
+ model.setPriority(1);
+ model.setProviderId(LDAPStorageProviderFactory.PROVIDER_NAME);
+ model.getConfig().addAll(ldapConfig);
+
+ ldapModel = appRealm.addComponentModel(model);
+ LDAPTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel);
+
+ // Delete all LDAP users and add some new for testing
+ LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
+ LDAPTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
+
+ // Remove the mapper for "username-cn" and create new mapper for fullName
+ ComponentModel mapperModel = LDAPTestUtils.getSubcomponentByName(appRealm, ldapModel, "username-cn");
+ Assert.assertNotNull(mapperModel);
+ appRealm.removeComponent(mapperModel);
+
+ mapperModel = KeycloakModelUtils.createComponentModel("fullNameWritable", ldapModel.getId(), FullNameLDAPStorageMapperFactory.PROVIDER_ID, LDAPStorageMapper.class.getName(),
+ FullNameLDAPStorageMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN,
+ FullNameLDAPStorageMapper.READ_ONLY, "false",
+ FullNameLDAPStorageMapper.WRITE_ONLY, "true");
+ appRealm.addComponentModel(mapperModel);
+
+ appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
+ }
+ });
+
+
+ @ClassRule
+ public static TestRule chain = RuleChain
+ .outerRule(ldapRule)
+ .around(keycloakRule);
+
+ @Rule
+ public WebRule webRule = new WebRule(this);
+
+ @WebResource
+ protected OAuthClient oauth;
+
+ @WebResource
+ protected WebDriver driver;
+
+ @WebResource
+ protected AppPage appPage;
+
+ @WebResource
+ protected RegisterPage registerPage;
+
+ @WebResource
+ protected LoginPage loginPage;
+
+ @WebResource
+ protected AccountUpdateProfilePage profilePage;
+
+ @WebResource
+ protected AccountPasswordPage changePasswordPage;
+
+
+
+// @Test
+// public void test01Sleep() throws Exception {
+// Thread.sleep(1000000);
+// }
+
+ @Test
+ public void test01_addUserWithoutFullName() {
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+ RealmModel appRealm = manager.getRealm("test");
+
+ UserModel john = session.users().addUser(appRealm, "johnkeycloak");
+ john.setEmail("johnkeycloak@email.cz");
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+ session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+ RealmModel appRealm = manager.getRealm("test");
+
+ UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
+ Assert.assertNotNull(john.getFederationLink());
+ assertDnStartsWith(session, john, "cn=johnkeycloak");
+
+ session.users().removeUser(appRealm, john);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+ }
+
+
+ @Test
+ public void test02_registerUserWithFullName() {
+
+ loginPage.open();
+ loginPage.clickRegister();
+ registerPage.assertCurrent();
+
+ registerPage.register("Johny", "Anthony", "johnyanth@check.cz", "johnkeycloak", "Password1", "Password1");
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmModel appRealm = session.realms().getRealmByName("test");
+ UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
+ assertUser(session, john, "johnkeycloak", "Johny", "Anthony", true, "cn=Johny Anthony");
+
+ session.users().removeUser(appRealm, john);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+
+ @Test
+ public void test03_addUserWithFirstNameOnly() {
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+ RealmModel appRealm = manager.getRealm("test");
+
+ UserModel john = session.users().addUser(appRealm, "johnkeycloak");
+ john.setEmail("johnkeycloak@email.cz");
+ john.setFirstName("Johnyyy");
+ john.setEnabled(true);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+ session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+ RealmModel appRealm = manager.getRealm("test");
+
+ UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
+ assertUser(session, john, "johnkeycloak", "Johnyyy", "", true, "cn=Johnyyy");
+
+ session.users().removeUser(appRealm, john);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+
+ @Test
+ public void test04_addUserWithLastNameOnly() {
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+ RealmModel appRealm = manager.getRealm("test");
+
+ UserModel john = session.users().addUser(appRealm, "johnkeycloak");
+ john.setEmail("johnkeycloak@email.cz");
+ john.setLastName("Anthonyy");
+ john.setEnabled(true);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+ session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+ RealmModel appRealm = manager.getRealm("test");
+
+ UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
+ assertUser(session, john, "johnkeycloak", "", "Anthonyy", true, "cn=Anthonyy");
+
+ session.users().removeUser(appRealm, john);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+
+ @Test
+ public void test05_registerUserWithFullNameSpecialChars() {
+
+ loginPage.open();
+ loginPage.clickRegister();
+ registerPage.assertCurrent();
+
+ registerPage.register("Jož,o", "Baříč", "johnyanth@check.cz", "johnkeycloak", "Password1", "Password1");
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmModel appRealm = session.realms().getRealmByName("test");
+ UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
+ assertUser(session, john, "johnkeycloak", "Jož,o", "Baříč", true, "cn=Jož\\,o Baříč");
+
+ session.users().removeUser(appRealm, john);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+
+ @Test
+ public void test06_conflicts() {
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+ RealmModel appRealm = manager.getRealm("test");
+
+ UserModel john = session.users().addUser(appRealm, "existingkc");
+ john.setFirstName("John");
+ john.setLastName("Existing");
+ john.setEnabled(true);
+
+ UserModel john2 = session.users().addUser(appRealm, "existingkc1");
+ john2.setEnabled(true);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+ loginPage.open();
+ loginPage.clickRegister();
+ registerPage.assertCurrent();
+
+ registerPage.register("John", "Existing", "johnyanth@check.cz", "existingkc", "Password1", "Password1");
+ Assert.assertEquals("Username already exists.", registerPage.getError());
+
+ registerPage.register("John", "Existing", "johnyanth@check.cz", "existingkc2", "Password1", "Password1");
+ appPage.logout();
+
+ loginPage.open();
+ loginPage.clickRegister();
+ registerPage.assertCurrent();
+ registerPage.register("John", "Existing", "johnyanth2@check.cz", "existingkc3", "Password1", "Password1");
+
+ session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+ RealmModel appRealm = manager.getRealm("test");
+
+ UserModel existingKc = session.users().getUserByUsername("existingkc", appRealm);
+ assertUser(session, existingKc, "existingkc", "John", "Existing", true, "cn=John Existing");
+
+ UserModel existingKc1 = session.users().getUserByUsername("existingkc1", appRealm);
+ assertUser(session, existingKc1, "existingkc1", "", "", true, "cn=existingkc1");
+
+ UserModel existingKc2 = session.users().getUserByUsername("existingkc2", appRealm);
+ assertUser(session, existingKc2, "existingkc2", "John", "Existing", true, "cn=John Existing0");
+
+ UserModel existingKc3 = session.users().getUserByUsername("existingkc3", appRealm);
+ assertUser(session, existingKc3, "existingkc3", "John", "Existing", true, "cn=John Existing1");
+
+ session.users().removeUser(appRealm, existingKc);
+ session.users().removeUser(appRealm, existingKc1);
+ session.users().removeUser(appRealm, existingKc2);
+ session.users().removeUser(appRealm, existingKc3);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+
+ private void assertUser(KeycloakSession session, UserModel user, String expectedUsername, String expectedFirstName, String expectedLastName, boolean expectedEnabled, String expectedDn) {
+ Assert.assertNotNull(user);
+ Assert.assertNotNull(user.getFederationLink());
+ Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
+ Assert.assertEquals(expectedUsername, user.getUsername());
+ Assert.assertEquals(expectedFirstName, user.getFirstName());
+ Assert.assertEquals(expectedLastName, user.getLastName());
+ Assert.assertEquals(expectedEnabled, user.isEnabled());
+ assertDnStartsWith(session, user, expectedDn);
+ }
+
+
+ private void assertDnStartsWith(KeycloakSession session, UserModel user, String expectedRDn) {
+ String usersDn = LDAPTestUtils.getLdapProvider(session, ldapModel).getLdapIdentityStore().getConfig().getUsersDn();
+ String userDN = user.getFirstAttribute(LDAPConstants.LDAP_ENTRY_DN);
+ Assert.assertTrue(userDN.equalsIgnoreCase(expectedRDn + "," + usersDn));
+ }
+
+}