KEYCLOAK-2403 Cannot create user in LDAP/AD from Keycloak using Full Name User Federation Mapper

This commit is contained in:
mposolda 2017-01-20 21:08:13 +01:00
parent fe8e437e74
commit 39f8311484
6 changed files with 512 additions and 11 deletions

View file

@ -27,7 +27,15 @@ import java.util.LinkedList;
*/ */
public class LDAPDn { 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) { public static LDAPDn fromString(String dnString) {
LDAPDn dn = new LDAPDn(); 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); LinkedList<Entry> parentDnEntries = new LinkedList<>(entries);
parentDnEntries.remove(); parentDnEntries.remove();
return toString(parentDnEntries); return new LDAPDn(parentDnEntries);
} }
public boolean isDescendantOf(LDAPDn expectedParentDn) { public boolean isDescendantOf(LDAPDn expectedParentDn) {

View file

@ -103,6 +103,8 @@ public class LDAPIdentityStore implements IdentityStore {
@Override @Override
public void update(LDAPObject ldapObject) { public void update(LDAPObject ldapObject) {
checkRename(ldapObject);
BasicAttributes updatedAttributes = extractAttributes(ldapObject, false); BasicAttributes updatedAttributes = extractAttributes(ldapObject, false);
NamingEnumeration<Attribute> attributes = updatedAttributes.getAll(); 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 @Override
public void remove(LDAPObject ldapObject) { public void remove(LDAPObject ldapObject) {
this.operationManager.removeEntry(ldapObject.getDn().toString()); this.operationManager.removeEntry(ldapObject.getDn().toString());

View file

@ -21,6 +21,7 @@ import org.jboss.logging.Logger;
import org.keycloak.models.LDAPConstants; import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException; import org.keycloak.models.ModelException;
import org.keycloak.storage.ldap.LDAPConfig; 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.idm.query.internal.LDAPQuery;
import org.keycloak.storage.ldap.mappers.LDAPOperationDecorator; import org.keycloak.storage.ldap.mappers.LDAPOperationDecorator;
@ -28,6 +29,7 @@ import javax.naming.AuthenticationException;
import javax.naming.Binding; import javax.naming.Binding;
import javax.naming.Context; import javax.naming.Context;
import javax.naming.InitialContext; import javax.naming.InitialContext;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
import javax.naming.NamingException; import javax.naming.NamingException;
import javax.naming.directory.Attribute; 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 { 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 List<SearchResult> result = new ArrayList<SearchResult>();
final SearchControls cons = getSearchControls(returningAttributes, searchScope); final SearchControls cons = getSearchControls(returningAttributes, searchScope);

View file

@ -75,7 +75,7 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
@Override @Override
public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser, RealmModel realm) { public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser, RealmModel realm) {
String ldapFullNameAttrName = getLdapFullNameAttrName(); String ldapFullNameAttrName = getLdapFullNameAttrName();
String fullName = getFullName(localUser.getFirstName(), localUser.getLastName()); String fullName = getFullNameForWriteToLDAP(localUser.getFirstName(), localUser.getLastName(), localUser.getUsername());
ldapUser.setSingleAttribute(ldapFullNameAttrName, fullName); ldapUser.setSingleAttribute(ldapFullNameAttrName, fullName);
if (isReadOnly()) { if (isReadOnly()) {
@ -103,7 +103,7 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
} }
private void setFullNameToLDAPObject() { private void setFullNameToLDAPObject() {
String fullName = getFullName(getFirstName(), getLastName()); String fullName = getFullNameForWriteToLDAP(getFirstName(), getLastName(), getUsername());
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.tracef("Pushing full name attribute to LDAP. Full name: %s", fullName); 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; return ldapFullNameAttrName == null ? LDAPConstants.CN : ldapFullNameAttrName;
} }
protected String getFullName(String firstName, String lastName) { protected String getFullNameForWriteToLDAP(String firstName, String lastName, String username) {
if (firstName != null && lastName != null) { if (!isBlank(firstName) && !isBlank(lastName)) {
return firstName + " " + lastName; return firstName + " " + lastName;
} else if (firstName != null) { } else if (!isBlank(firstName)) {
return firstName; return firstName;
} else if (lastName != null) { } else if (!isBlank(lastName)) {
return lastName; return lastName;
} else if (isFallbackToUsername()) {
return username;
} else { } else {
return LDAPConstants.EMPTY_ATTRIBUTE_VALUE; return LDAPConstants.EMPTY_ATTRIBUTE_VALUE;
} }
} }
private boolean isBlank(String attr) {
return attr == null || attr.trim().isEmpty();
}
private boolean isReadOnly() { private boolean isReadOnly() {
return parseBooleanParameter(mapperModel, READ_ONLY); return parseBooleanParameter(mapperModel, READ_ONLY);
} }
@ -196,4 +202,11 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
private boolean isWriteOnly() { private boolean isWriteOnly() {
return parseBooleanParameter(mapperModel, WRITE_ONLY); 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);
}
} }

View file

@ -35,7 +35,7 @@ public class LDAPDnTest {
Assert.assertEquals("uid=Johny\\,Depp\\+Pepp\\\\Foo,ou=People,dc=keycloak,dc=org", dn.toString()); 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(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=keycloak, dc=org")));
Assert.assertTrue(dn.isDescendantOf(LDAPDn.fromString("dc=org"))); Assert.assertTrue(dn.isDescendantOf(LDAPDn.fromString("dc=org")));
@ -46,4 +46,22 @@ public class LDAPDnTest {
Assert.assertEquals("uid", dn.getFirstRdnAttrName()); Assert.assertEquals("uid", dn.getFirstRdnAttrName());
Assert.assertEquals("Johny,Depp+Pepp\\Foo", dn.getFirstRdnAttrValue()); 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());
}
} }

View file

@ -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));
}
}