KEYCLOAK-599 Added UserFederationMappers. Added UserAttributeLDAPFederationMapper

This commit is contained in:
mposolda
2015-05-13 08:41:55 +02:00
parent 61c35265a6
commit 1490f106f2
55 changed files with 1886 additions and 1985 deletions
@@ -22,7 +22,7 @@ public class DummyUserFederationProvider implements UserFederationProvider {
private static Map<String, UserModel> users = new HashMap<String, UserModel>();
@Override
public UserModel proxy(UserModel local) {
public UserModel validateAndProxy(RealmModel realm, UserModel local) {
return local;
}
@@ -68,7 +68,7 @@ public class DummyUserFederationProvider implements UserFederationProvider {
}
@Override
public boolean isValid(UserModel local) {
public boolean isValid(RealmModel realm, UserModel local) {
return false;
}
@@ -12,8 +12,7 @@ import org.keycloak.OAuth2Constants;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPUser;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelReadOnlyException;
@@ -23,6 +22,7 @@ import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.UserModelDelegate;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.OAuthClient;
@@ -53,7 +53,7 @@ public class FederationProvidersIntegrationTest {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
addUser(manager.getSession(), appRealm, "mary", "mary@test.com", "password-app");
FederationTestUtils.addLocalUser(manager.getSession(), appRealm, "mary", "mary@test.com", "password-app");
Map<String,String> ldapConfig = ldapRule.getConfig();
ldapConfig.put(LDAPConstants.SYNC_REGISTRATIONS, "true");
@@ -62,13 +62,13 @@ public class FederationProvidersIntegrationTest {
ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap", -1, -1, 0);
// Delete all LDAP users and add some new for testing
LDAPIdentityStore ldapStore = getLdapIdentityStore(manager.getSession(), ldapModel);
LDAPUtils.removeAllUsers(ldapStore);
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPUtils.removeAllUsers(ldapFedProvider, appRealm);
LDAPUser john = LDAPUtils.addUser(ldapStore, "johnkeycloak", "John", "Doe", "john@email.org");
LDAPUtils.updatePassword(ldapStore, john, "Password1");
LDAPObject john = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", "1234");
ldapFedProvider.getLdapIdentityStore().updatePassword(john, "Password1");
LDAPUser existing = LDAPUtils.addUser(ldapStore, "existing", "Existing", "Foo", "existing@email.org");
LDAPObject existing = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "existing", "Existing", "Foo", "existing@email.org", "5678");
}
});
@@ -108,18 +108,6 @@ public class FederationProvidersIntegrationTest {
//
// }
static UserModel addUser(KeycloakSession session, RealmModel realm, String username, String email, String password) {
UserModel user = session.users().addUser(realm, username);
user.setEmail(email);
user.setEnabled(true);
UserCredentialModel creds = new UserCredentialModel();
creds.setType(CredentialRepresentation.PASSWORD);
creds.setValue(password);
user.updateCredential(creds);
return user;
}
@Test
public void caseSensitiveSearch() {
@@ -128,9 +116,6 @@ public class FederationProvidersIntegrationTest {
// This should fail for now due to case-sensitivity
loginPage.login("johnKeycloak", "Password1");
Assert.assertEquals("Invalid username or password.", loginPage.getError());
loginPage.login("John@email.org", "Password1");
Assert.assertEquals("Invalid username or password.", loginPage.getError());
}
@Test
@@ -191,6 +176,7 @@ public class FederationProvidersIntegrationTest {
Assert.assertEquals("John", profilePage.getFirstName());
Assert.assertEquals("Doe", profilePage.getLastName());
Assert.assertEquals("john@email.org", profilePage.getEmail());
Assert.assertEquals("1234", profilePage.getPostalCode());
}
@Test
@@ -257,7 +243,7 @@ public class FederationProvidersIntegrationTest {
loginPage.clickRegister();
registerPage.assertCurrent();
registerPage.register("firstName", "lastName", "email2@check.cz", "registerUserSuccess2", "Password1", "Password1");
registerPage.register("firstName", "lastName", "email2@check.cz", "registerUserSuccess2", "Password1", "Password1", "non-LDAP-Mapped street", null, null, "78910", null);
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
KeycloakSession session = keycloakRule.startSession();
@@ -267,6 +253,8 @@ public class FederationProvidersIntegrationTest {
Assert.assertNotNull(user);
Assert.assertNotNull(user.getFederationLink());
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
Assert.assertEquals("78910", user.getAttribute("postal_code"));
Assert.assertEquals("non-LDAP-Mapped street", user.getAttribute("street"));
} finally {
keycloakRule.stopSession(session, false);
}
@@ -346,13 +334,14 @@ public class FederationProvidersIntegrationTest {
@Test
public void testSearch() {
KeycloakSession session = keycloakRule.startSession();
LDAPIdentityStore ldapStore = getLdapIdentityStore(session, ldapModel);
try {
RealmModel appRealm = session.realms().getRealmByName("test");
LDAPUtils.addUser(ldapStore, "username1", "John1", "Doel1", "user1@email.org");
LDAPUtils.addUser(ldapStore, "username2", "John2", "Doel2", "user2@email.org");
LDAPUtils.addUser(ldapStore, "username3", "John3", "Doel3", "user3@email.org");
LDAPUtils.addUser(ldapStore, "username4", "John4", "Doel4", "user4@email.org");
LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username1", "John1", "Doel1", "user1@email.org", "121");
FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username2", "John2", "Doel2", "user2@email.org", "122");
FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username3", "John3", "Doel3", "user3@email.org", "123");
FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username4", "John4", "Doel4", "user4@email.org", "124");
// Users are not at local store at this moment
Assert.assertNull(session.userStorage().getUserByUsername("username1", appRealm));
@@ -362,19 +351,19 @@ public class FederationProvidersIntegrationTest {
// search by username
session.users().searchForUser("username1", appRealm);
SyncProvidersTest.assertUserImported(session.userStorage(), appRealm, "username1", "John1", "Doel1", "user1@email.org");
FederationTestUtils.assertUserImported(session.userStorage(), appRealm, "username1", "John1", "Doel1", "user1@email.org", "121");
// search by email
session.users().searchForUser("user2@email.org", appRealm);
SyncProvidersTest.assertUserImported(session.userStorage(), appRealm, "username2", "John2", "Doel2", "user2@email.org");
FederationTestUtils.assertUserImported(session.userStorage(), appRealm, "username2", "John2", "Doel2", "user2@email.org", "122");
// search by lastName
session.users().searchForUser("Doel3", appRealm);
SyncProvidersTest.assertUserImported(session.userStorage(), appRealm, "username3", "John3", "Doel3", "user3@email.org");
FederationTestUtils.assertUserImported(session.userStorage(), appRealm, "username3", "John3", "Doel3", "user3@email.org", "123");
// search by firstName + lastName
session.users().searchForUser("John4 Doel4", appRealm);
SyncProvidersTest.assertUserImported(session.userStorage(), appRealm, "username4", "John4", "Doel4", "user4@email.org");
FederationTestUtils.assertUserImported(session.userStorage(), appRealm, "username4", "John4", "Doel4", "user4@email.org", "124");
} finally {
keycloakRule.stopSession(session, true);
}
@@ -402,7 +391,9 @@ public class FederationProvidersIntegrationTest {
Assert.assertTrue(session.users().validCredentials(appRealm, user, cred));
// LDAP password is still unchanged
Assert.assertTrue(LDAPUtils.validatePassword(getLdapIdentityStore(session, model), user, "Password1"));
LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, model);
LDAPObject ldapUser = ldapProvider.loadLDAPUserByUsername(appRealm, "johnkeycloak");
ldapProvider.getLdapIdentityStore().validatePassword(ldapUser, "Password1");
// ATM it's not permitted to delete user in unsynced mode. Should be user deleted just locally instead?
Assert.assertFalse(session.users().removeUser(appRealm, user));
@@ -419,10 +410,4 @@ public class FederationProvidersIntegrationTest {
}
}
static LDAPIdentityStore getLdapIdentityStore(KeycloakSession keycloakSession, UserFederationProviderModel ldapFedModel) {
LDAPFederationProviderFactory ldapProviderFactory = (LDAPFederationProviderFactory) keycloakSession.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, ldapFedModel.getProviderName());
LDAPFederationProvider ldapProvider = ldapProviderFactory.getInstance(keycloakSession, ldapFedModel);
return ldapProvider.getLdapIdentityStore();
}
}
@@ -0,0 +1,85 @@
package org.keycloak.testsuite.federation;
import org.junit.Assert;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.models.utils.UserModelDelegate;
import org.keycloak.representations.idm.CredentialRepresentation;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
class FederationTestUtils {
public static UserModel addLocalUser(KeycloakSession session, RealmModel realm, String username, String email, String password) {
UserModel user = session.users().addUser(realm, username);
user.setEmail(email);
user.setEnabled(true);
UserCredentialModel creds = new UserCredentialModel();
creds.setType(CredentialRepresentation.PASSWORD);
creds.setValue(password);
user.updateCredential(creds);
return user;
}
public static LDAPObject addLDAPUser(LDAPFederationProvider ldapProvider, RealmModel realm, final String username,
final String firstName, final String lastName, final String email, final String postalCode) {
UserModel helperUser = new UserModelDelegate(null) {
@Override
public String getUsername() {
return username;
}
@Override
public String getFirstName() {
return firstName;
}
@Override
public String getLastName() {
return lastName;
}
@Override
public String getEmail() {
return email;
}
@Override
public String getAttribute(String name) {
if (name == "postal_code") {
return postalCode;
} else {
return null;
}
}
};
return LDAPUtils.addUserToLDAP(ldapProvider, realm, helperUser);
}
public static LDAPFederationProvider getLdapProvider(KeycloakSession keycloakSession, UserFederationProviderModel ldapFedModel) {
LDAPFederationProviderFactory ldapProviderFactory = (LDAPFederationProviderFactory) keycloakSession.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, ldapFedModel.getProviderName());
return ldapProviderFactory.getInstance(keycloakSession, ldapFedModel);
}
public static void assertUserImported(UserProvider userProvider, RealmModel realm, String username, String expectedFirstName, String expectedLastName, String expectedEmail, String expectedPostalCode) {
UserModel user = userProvider.getUserByUsername(username, realm);
Assert.assertNotNull(user);
Assert.assertEquals(expectedFirstName, user.getFirstName());
Assert.assertEquals(expectedLastName, user.getLastName());
Assert.assertEquals(expectedEmail, user.getEmail());
Assert.assertEquals(expectedPostalCode, user.getAttribute("postal_code"));
}
}
@@ -7,10 +7,10 @@ import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runners.MethodSorters;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPUser;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.LDAPConstants;
@@ -57,12 +57,12 @@ public class SyncProvidersTest {
-1, -1, 0);
// Delete all LDAP users and add 5 new users for testing
LDAPIdentityStore ldapStore = FederationProvidersIntegrationTest.getLdapIdentityStore(manager.getSession(), ldapModel);
LDAPUtils.removeAllUsers(ldapStore);
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPUtils.removeAllUsers(ldapFedProvider, appRealm);
for (int i=1 ; i<=5 ; i++) {
LDAPUser user = LDAPUtils.addUser(ldapStore, "user" + i, "User" + i + "FN", "User" + i + "LN", "user" + i + "@email.org");
LDAPUtils.updatePassword(ldapStore, user, "Password1");
LDAPObject ldapUser = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "user" + i, "User" + i + "FN", "User" + i + "LN", "user" + i + "@email.org", "12" + i);
ldapFedProvider.getLdapIdentityStore().updatePassword(ldapUser, "Password1");
}
// Add dummy provider
@@ -96,11 +96,11 @@ public class SyncProvidersTest {
RealmModel testRealm = session.realms().getRealm("test");
UserProvider userProvider = session.userStorage();
// Assert users imported
assertUserImported(userProvider, testRealm, "user1", "User1FN", "User1LN", "user1@email.org");
assertUserImported(userProvider, testRealm, "user2", "User2FN", "User2LN", "user2@email.org");
assertUserImported(userProvider, testRealm, "user3", "User3FN", "User3LN", "user3@email.org");
assertUserImported(userProvider, testRealm, "user4", "User4FN", "User4LN", "user4@email.org");
assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5@email.org");
FederationTestUtils.assertUserImported(userProvider, testRealm, "user1", "User1FN", "User1LN", "user1@email.org", "121");
FederationTestUtils.assertUserImported(userProvider, testRealm, "user2", "User2FN", "User2LN", "user2@email.org", "122");
FederationTestUtils.assertUserImported(userProvider, testRealm, "user3", "User3FN", "User3LN", "user3@email.org", "123");
FederationTestUtils.assertUserImported(userProvider, testRealm, "user4", "User4FN", "User4LN", "user4@email.org", "124");
FederationTestUtils.assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5@email.org", "125");
// Assert lastSync time updated
Assert.assertTrue(ldapModel.getLastSync() > 0);
@@ -117,12 +117,16 @@ public class SyncProvidersTest {
sleep(1000);
// Add user to LDAP and update 'user5' in LDAP
LDAPIdentityStore ldapStore = FederationProvidersIntegrationTest.getLdapIdentityStore(session, ldapModel);
LDAPUtils.addUser(ldapStore, "user6", "User6FN", "User6LN", "user6@email.org");
LDAPUtils.updateUser(ldapStore, "user5", "User5FNUpdated", "User5LNUpdated", "user5Updated@email.org");
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
FederationTestUtils.addLDAPUser(ldapFedProvider, testRealm, "user6", "User6FN", "User6LN", "user6@email.org", "126");
LDAPObject ldapUser5 = ldapFedProvider.loadLDAPUserByUsername(testRealm, "user5");
// NOTE: Changing LDAP attributes directly here
ldapUser5.setAttribute(LDAPConstants.EMAIL, "user5Updated@email.org");
ldapUser5.setAttribute(LDAPConstants.POSTAL_CODE, "521");
ldapFedProvider.getLdapIdentityStore().update(ldapUser5);
// Assert still old users in local provider
assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5@email.org");
FederationTestUtils.assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5@email.org", "125");
Assert.assertNull(userProvider.getUserByUsername("user6", testRealm));
// Trigger partial sync
@@ -138,8 +142,8 @@ public class SyncProvidersTest {
RealmModel testRealm = session.realms().getRealm("test");
UserProvider userProvider = session.userStorage();
// Assert users updated in local provider
assertUserImported(userProvider, testRealm, "user5", "User5FNUpdated", "User5LNUpdated", "user5Updated@email.org");
assertUserImported(userProvider, testRealm, "user6", "User6FN", "User6LN", "user6@email.org");
FederationTestUtils.assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5Updated@email.org", "521");
FederationTestUtils.assertUserImported(userProvider, testRealm, "user6", "User6FN", "User6LN", "user6@email.org", "126");
} finally {
keycloakRule.stopSession(session, false);
}
@@ -189,12 +193,4 @@ public class SyncProvidersTest {
Assert.assertEquals(syncResult.getUpdated(), expectedUpdated);
Assert.assertEquals(syncResult.getRemoved(), expectedRemoved);
}
public static void assertUserImported(UserProvider userProvider, RealmModel realm, String username, String expectedFirstName, String expectedLastName, String expectedEmail) {
UserModel user = userProvider.getUserByUsername(username, realm);
Assert.assertNotNull(user);
Assert.assertEquals(expectedFirstName, user.getFirstName());
Assert.assertEquals(expectedLastName, user.getLastName());
Assert.assertEquals(expectedEmail, user.getEmail());
}
}
@@ -28,10 +28,9 @@ public class LDAPConfiguration {
static {
PROP_MAPPINGS.put(LDAPConstants.CONNECTION_URL, "idm.test.ldap.connection.url");
PROP_MAPPINGS.put(LDAPConstants.BASE_DN, "idm.test.ldap.base.dn");
PROP_MAPPINGS.put("rolesDnSuffix", "idm.test.ldap.roles.dn.suffix");
PROP_MAPPINGS.put("groupDnSuffix", "idm.test.ldap.group.dn.suffix");
PROP_MAPPINGS.put(LDAPConstants.USER_DN_SUFFIX, "idm.test.ldap.user.dn.suffix");
PROP_MAPPINGS.put(LDAPConstants.USER_DNS, "idm.test.ldap.user.dn.suffix");
PROP_MAPPINGS.put(LDAPConstants.BIND_DN, "idm.test.ldap.bind.dn");
PROP_MAPPINGS.put(LDAPConstants.BIND_CREDENTIAL, "idm.test.ldap.bind.credential");
PROP_MAPPINGS.put(LDAPConstants.VENDOR, "idm.test.ldap.vendor");
@@ -53,10 +52,9 @@ public class LDAPConfiguration {
PROP_MAPPINGS.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "idm.test.kerberos.use.kerberos.for.password.authentication");
DEFAULT_VALUES.put(LDAPConstants.CONNECTION_URL, "ldap://localhost:10389");
DEFAULT_VALUES.put(LDAPConstants.BASE_DN, "dc=keycloak,dc=org");
DEFAULT_VALUES.put("rolesDnSuffix", "ou=Roles,dc=keycloak,dc=org");
DEFAULT_VALUES.put("groupDnSuffix", "ou=Groups,dc=keycloak,dc=org");
DEFAULT_VALUES.put(LDAPConstants.USER_DN_SUFFIX, "ou=People,dc=keycloak,dc=org");
DEFAULT_VALUES.put(LDAPConstants.USER_DNS, "ou=People,dc=keycloak,dc=org");
DEFAULT_VALUES.put(LDAPConstants.BIND_DN, "uid=admin,ou=system");
DEFAULT_VALUES.put(LDAPConstants.BIND_CREDENTIAL, "secret");
DEFAULT_VALUES.put(LDAPConstants.VENDOR, LDAPConstants.VENDOR_OTHER);
@@ -44,6 +44,9 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
@FindBy(id = "email")
private WebElement emailInput;
@FindBy(id = "user.attributes.postal_code")
private WebElement postalCodeInput;
@FindBy(id = "referrer")
private WebElement backToApplicationLink;
@@ -84,6 +87,10 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
return lastNameInput.getAttribute("value");
}
public String getPostalCode() {
return postalCodeInput.getAttribute("value");
}
public String getEmail() {
return emailInput.getAttribute("value");
}
@@ -23,6 +23,7 @@ package org.keycloak.testsuite.pages;
import org.junit.Assert;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@@ -56,6 +57,25 @@ public class RegisterPage extends AbstractPage {
@FindBy(className = "feedback-error")
private WebElement loginErrorMessage;
public void register(String firstName, String lastName, String email, String username, String password, String passwordConfirm,
String street, String cityOrLocality, String stateOrRegion, String zipOrPostalCode, String country) {
fillExtendedField("street", street);
fillExtendedField("locality", cityOrLocality);
fillExtendedField("region", stateOrRegion);
fillExtendedField("postal_code", zipOrPostalCode);
fillExtendedField("country", country);
register(firstName, lastName, email, username, password, passwordConfirm);
}
private void fillExtendedField(String fieldName, String value) {
WebElement field = driver.findElement(By.id("user.attributes." + fieldName));
field.clear();
if (value != null) {
field.sendKeys(value);
}
}
public void register(String firstName, String lastName, String email, String username, String password, String passwordConfirm) {
firstNameInput.clear();
if (firstName != null) {