KEYCLOAK-2403 Cannot create user in LDAP/AD from Keycloak using Full Name User Federation Mapper
This commit is contained in:
parent
fe8e437e74
commit
39f8311484
6 changed files with 512 additions and 11 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue