/*
* 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 org.jboss.logging.Logger;
import org.junit.Assert;
import org.junit.Before;
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.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel;
import org.keycloak.models.Constants;
import org.keycloak.storage.ReadOnlyException;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.ldap.LDAPConfig;
import org.keycloak.storage.ldap.LDAPStorageProvider;
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
import org.keycloak.storage.ldap.idm.model.LDAPObject;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapper;
import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapperFactory;
import org.keycloak.storage.ldap.mappers.HardcodedLDAPAttributeMapper;
import org.keycloak.storage.ldap.mappers.HardcodedLDAPAttributeMapperFactory;
import org.keycloak.storage.ldap.mappers.HardcodedLDAPRoleStorageMapper;
import org.keycloak.storage.ldap.mappers.HardcodedLDAPRoleStorageMapperFactory;
import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper;
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.OAuthGrantPage;
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;
import java.util.List;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MASTER;
import static org.junit.Assert.assertEquals;
import static org.keycloak.models.AdminRoles.ADMIN;
import static org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class LDAPProvidersIntegrationTest {
private static final Logger log = Logger.getLogger(LDAPProvidersIntegrationTest.class);
private static LDAPRule ldapRule = new LDAPRule();
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(0);
model.setProviderId(LDAPStorageProviderFactory.PROVIDER_NAME);
model.setConfig(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);
LDAPObject john = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, john, "Password1");
LDAPObject existing = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "existing", "Existing", "Foo", "existing@email.org", null, "5678");
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;
@WebResource
protected OAuthGrantPage grantPage;
// @Test
// @Ignore
// public void runit() throws Exception {
// Thread.sleep(10000000);
//
// }
/**
* KEYCLOAK-3986
*
*/
@Test
public void testSyncRegistrationOff() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
UserStorageProviderModel newModel = new UserStorageProviderModel(ldapModel);
newModel.getConfig().putSingle(LDAPConstants.SYNC_REGISTRATIONS, "false");
appRealm.updateComponent(newModel);
UserModel newUser1 = session.users().addUser(appRealm, "newUser1");
Assert.assertNull(newUser1.getFederationLink());
} finally {
keycloakRule.stopSession(session, false);
}
}
private Keycloak adminClient;
@Before
public void onBefore() {
adminClient = Keycloak.getInstance(AUTH_SERVER_ROOT, MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID);
}
@Test
public void testRemoveImportedUsers() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm);
Assert.assertEquals(ldapModel.getId(), user.getFederationLink());
} finally {
keycloakRule.stopSession(session, true);
}
adminClient.realm("test").userStorage().removeImportedUsers(ldapModel.getId());
session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
UserModel user = session.userLocalStorage().getUserByUsername("johnkeycloak", appRealm);
Assert.assertNull(user);
} finally {
keycloakRule.stopSession(session, true);
}
}
// test name prefixed with zz to make sure it runs last as we are unlinking imported users
@Test
public void zzTestUnlinkUsers() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm);
Assert.assertEquals(ldapModel.getId(), user.getFederationLink());
} finally {
keycloakRule.stopSession(session, true);
}
adminClient.realm("test").userStorage().unlink(ldapModel.getId());
session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm);
Assert.assertNotNull(user);
Assert.assertNull(user.getFederationLink());
} finally {
keycloakRule.stopSession(session, true);
}
}
@Test
public void caseInSensitiveImport() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
LDAPObject jbrown2 = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "JBrown2", "John", "Brown2", "jbrown2@email.org", null, "1234");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, jbrown2, "Password1");
LDAPObject jbrown3 = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "jbrown3", "John", "Brown3", "JBrown3@email.org", null, "1234");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, jbrown3, "Password1");
} finally {
keycloakRule.stopSession(session, true);
}
loginSuccessAndLogout("jbrown2", "Password1");
loginSuccessAndLogout("JBrown2", "Password1");
loginSuccessAndLogout("jbrown2@email.org", "Password1");
loginSuccessAndLogout("JBrown2@email.org", "Password1");
loginSuccessAndLogout("jbrown3", "Password1");
loginSuccessAndLogout("JBrown3", "Password1");
loginSuccessAndLogout("jbrown3@email.org", "Password1");
loginSuccessAndLogout("JBrown3@email.org", "Password1");
}
private void loginSuccessAndLogout(String username, String password) {
loginPage.open();
loginPage.login(username, password);
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
oauth.openLogout();
}
@Test
public void caseInsensitiveSearch() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
LDAPObject jbrown4 = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "JBrown4", "John", "Brown4", "jbrown4@email.org", null, "1234");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, jbrown4, "Password1");
LDAPObject jbrown5 = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "jbrown5", "John", "Brown5", "JBrown5@Email.org", null, "1234");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, jbrown5, "Password1");
} finally {
keycloakRule.stopSession(session, true);
}
session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
// search by username
List<UserModel> users = session.users().searchForUser("JBROwn4", appRealm);
Assert.assertEquals(1, users.size());
UserModel user4 = users.get(0);
Assert.assertEquals("jbrown4", user4.getUsername());
Assert.assertEquals("jbrown4@email.org", user4.getEmail());
// search by email
users = session.users().searchForUser("JBROwn5@eMAil.org", appRealm);
Assert.assertEquals(1, users.size());
UserModel user5 = users.get(0);
Assert.assertEquals("jbrown5", user5.getUsername());
Assert.assertEquals("jbrown5@email.org", user5.getEmail());
} finally {
keycloakRule.stopSession(session, true);
}
}
@Test
public void deleteFederationLink() throws Exception {
// KEYCLOAK-4789: Login in client, which requires consent
oauth.clientId("third-party");
loginPage.open();
loginPage.login("johnkeycloak", "Password1");
grantPage.assertCurrent();
grantPage.accept();
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
{
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
appRealm.removeComponent(ldapModel);
Assert.assertEquals(0, appRealm.getComponents(appRealm.getId(), UserStorageProvider.class.getName()).size());
} finally {
keycloakRule.stopSession(session, true);
}
}
loginPage.open();
loginPage.login("johnkeycloak", "Password1");
loginPage.assertCurrent();
Assert.assertEquals("Invalid username or password.", loginPage.getError());
{
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
ldapModel.setId(null);
ldapModel = appRealm.addComponentModel(ldapModel);
LDAPTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel);
} finally {
keycloakRule.stopSession(session, true);
}
}
oauth.clientId("test-app");
loginLdap();
}
@Test
public void loginClassic() {
loginPage.open();
loginPage.login("marykeycloak", "password-app");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
}
@Test
public void loginLdap() {
loginPage.open();
loginPage.login("johnkeycloak", "Password1");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
profilePage.open();
Assert.assertEquals("John", profilePage.getFirstName());
Assert.assertEquals("Doe", profilePage.getLastName());
Assert.assertEquals("john@email.org", profilePage.getEmail());
}
@Test
public void loginLdapWithDirectGrant() throws Exception {
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "johnkeycloak", "Password1");
assertEquals(200, response.getStatusCode());
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
response = oauth.doGrantAccessTokenRequest("password", "johnkeycloak", "");
assertEquals(401, response.getStatusCode());
}
@Test
public void loginLdapWithEmail() {
loginPage.open();
loginPage.login("john@email.org", "Password1");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
}
@Test
public void loginLdapWithoutPassword() {
loginPage.open();
loginPage.login("john@email.org", "");
Assert.assertEquals("Invalid username or password.", loginPage.getError());
}
@Test
public void passwordChangeLdap() throws Exception {
changePasswordPage.open();
loginPage.login("johnkeycloak", "Password1");
changePasswordPage.changePassword("Password1", "New-password1", "New-password1");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
changePasswordPage.logout();
loginPage.open();
loginPage.login("johnkeycloak", "Bad-password1");
Assert.assertEquals("Invalid username or password.", loginPage.getError());
loginPage.open();
loginPage.login("johnkeycloak", "New-password1");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
// Change password back to previous value
changePasswordPage.open();
changePasswordPage.changePassword("New-password1", "Password1", "Password1");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
}
@Test
public void registerExistingLdapUser() {
loginPage.open();
loginPage.clickRegister();
registerPage.assertCurrent();
// check existing username
registerPage.register("firstName", "lastName", "email@mail.cz", "existing", "Password1", "Password1");
registerPage.assertCurrent();
Assert.assertEquals("Username already exists.", registerPage.getError());
// Check existing email
registerPage.register("firstName", "lastName", "existing@email.org", "nonExisting", "Password1", "Password1");
registerPage.assertCurrent();
Assert.assertEquals("Email already exists.", registerPage.getError());
}
//
// KEYCLOAK-4533
//
@Test
public void testLDAPUserDeletionImport() {
KeycloakSession session = keycloakRule.startSession();
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
LDAPConfig config = ldapProvider.getLdapIdentityStore().getConfig();
// Make sure mary is gone
LDAPTestUtils.removeLDAPUserByUsername(ldapProvider, appRealm, config, "maryjane");
// Create the user in LDAP and register him
LDAPObject mary = LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "maryjane", "mary", "yram", "mj@testing.redhat.cz", null, "12398");
LDAPTestUtils.updateLDAPPassword(ldapProvider, mary, "Password1");
try {
// Log in and out of the user
loginSuccessAndLogout("maryjane", "Password1");
// Delete LDAP User
LDAPTestUtils.removeLDAPUserByUsername(ldapProvider, appRealm, config, "maryjane");
// Make sure the deletion took place.
List<UserModel> deletedUsers = session.users().searchForUser("mary yram", appRealm);
Assert.assertTrue(deletedUsers.isEmpty());
} finally {
keycloakRule.stopSession(session, false);
}
}
@Test
public void registerUserLdapSuccess() {
loginPage.open();
loginPage.clickRegister();
registerPage.assertCurrent();
registerPage.register("firstName", "lastName", "email2@check.cz", "registerUserSuccess2", "Password1", "Password1");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
UserModel user = session.users().getUserByUsername("registerUserSuccess2", appRealm);
Assert.assertNotNull(user);
Assert.assertNotNull(user.getFederationLink());
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
Assert.assertEquals("registerusersuccess2", user.getUsername());
Assert.assertEquals("firstName", user.getFirstName());
Assert.assertEquals("lastName", user.getLastName());
Assert.assertTrue(user.isEnabled());
} finally {
keycloakRule.stopSession(session, false);
}
}
@Test
public void testCaseSensitiveAttributeName() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
LDAPObject johnZip = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnzip", "John", "Zip", "johnzip@email.org", null, "12398");
// Remove default zipcode mapper and add the mapper for "POstalCode" to test case sensitivity
ComponentModel currentZipMapper = LDAPTestUtils.getSubcomponentByName(appRealm, ldapModel, "zipCodeMapper");
appRealm.removeComponent(currentZipMapper);
LDAPTestUtils.addUserAttributeMapper(appRealm, ldapModel, "zipCodeMapper-cs", "postal_code", "POstalCode");
// Fetch user from LDAP and check that postalCode is filled
UserModel user = session.users().getUserByUsername("johnzip", appRealm);
String postalCode = user.getFirstAttribute("postal_code");
Assert.assertEquals("12398", postalCode);
} finally {
keycloakRule.stopSession(session, false);
}
}
@Test
public void testCommaInUsername() {
KeycloakSession session = keycloakRule.startSession();
boolean skip = false;
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
// Workaround as comma is not allowed in sAMAccountName on active directory. So we will skip the test for this configuration
LDAPConfig config = ldapFedProvider.getLdapIdentityStore().getConfig();
if (config.isActiveDirectory() && config.getUsernameLdapAttribute().equals(LDAPConstants.SAM_ACCOUNT_NAME)) {
skip = true;
}
if (!skip) {
LDAPObject johnComma = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "john,comma", "John", "Comma", "johncomma@email.org", null, "12387");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, johnComma, "Password1");
LDAPObject johnPlus = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "john+plus,comma", "John", "Plus", "johnplus@email.org", null, "12387");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, johnPlus, "Password1");
}
} finally {
keycloakRule.stopSession(session, false);
}
if (!skip) {
// Try to import the user with comma in username into Keycloak
loginSuccessAndLogout("john,comma", "Password1");
loginSuccessAndLogout("john+plus,comma", "Password1");
}
}
//@Test // don't think we should support this, bburke
public void testDirectLDAPUpdate() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
LDAPObject johnDirect = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johndirect", "John", "Direct", "johndirect@email.org", null, "12399");
// Fetch user from LDAP and check that postalCode is filled
UserModel user = session.users().getUserByUsername("johndirect", appRealm);
String postalCode = user.getFirstAttribute("postal_code");
Assert.assertEquals("12399", postalCode);
// Directly update user in LDAP
johnDirect.setSingleAttribute(LDAPConstants.POSTAL_CODE, "12400");
johnDirect.setSingleAttribute(LDAPConstants.SN, "DirectLDAPUpdated");
ldapFedProvider.getLdapIdentityStore().update(johnDirect);
} finally {
keycloakRule.stopSession(session, true);
}
session = keycloakRule.startSession();
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
UserModel user = session.users().getUserByUsername("johndirect", appRealm);
// Verify that postalCode is still the same as we read it's value from Keycloak DB
user = session.users().getUserByUsername("johndirect", appRealm);
String postalCode = user.getFirstAttribute("postal_code");
Assert.assertEquals("12399", postalCode);
// Check user.getAttributes()
postalCode = user.getAttributes().get("postal_code").get(0);
Assert.assertEquals("12399", postalCode);
// LastName is new as lastName mapper will read the value from LDAP
String lastName = user.getLastName();
Assert.assertEquals("DirectLDAPUpdated", lastName);
} finally {
keycloakRule.stopSession(session, true);
}
session = keycloakRule.startSession();
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
// Update postalCode mapper to always read the value from LDAP
ComponentModel zipMapper = LDAPTestUtils.getSubcomponentByName(appRealm, ldapModel, "zipCodeMapper");
zipMapper.getConfig().putSingle(UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "true");
appRealm.updateComponent(zipMapper);
// Update lastName mapper to read the value from Keycloak DB
ComponentModel lastNameMapper = LDAPTestUtils.getSubcomponentByName(appRealm, ldapModel, "last name");
lastNameMapper.getConfig().putSingle(UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false");
appRealm.updateComponent(lastNameMapper);
// Verify that postalCode is read from LDAP now
UserModel user = session.users().getUserByUsername("johndirect", appRealm);
String postalCode = user.getFirstAttribute("postal_code");
Assert.assertEquals("12400", postalCode);
// Check user.getAttributes()
postalCode = user.getAttributes().get("postal_code").get(0);
Assert.assertEquals("12400", postalCode);
Assert.assertFalse(user.getAttributes().containsKey(UserModel.LAST_NAME));
// lastName is read from Keycloak DB now
String lastName = user.getLastName();
Assert.assertEquals("Direct", lastName);
} finally {
keycloakRule.stopSession(session, false);
}
}
// TODO: Rather separate test for fullNameMapper to better test all the possibilities
@Test
public void testFullNameMapper() {
KeycloakSession session = keycloakRule.startSession();
ComponentModel firstNameMapper = null;
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
// assert that user "fullnameUser" is not in local DB
Assert.assertNull(session.users().getUserByUsername("fullname", appRealm));
// Add the user with some fullName into LDAP directly. Ensure that fullName is saved into "cn" attribute in LDAP (currently mapped to model firstName)
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "fullname", "James Dee", "Dee", "fullname@email.org", null, "4578");
// add fullname mapper to the provider and remove "firstNameMapper". For this test, we will simply map full name to the LDAP attribute, which was before firstName ( "givenName" on active directory, "cn" on other LDAP servers)
firstNameMapper = LDAPTestUtils.getSubcomponentByName(appRealm, ldapModel, "first name");
String ldapFirstNameAttributeName = firstNameMapper.getConfig().getFirst(UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE);
appRealm.removeComponent(firstNameMapper);
ComponentModel fullNameMapperModel = KeycloakModelUtils.createComponentModel("full name", ldapModel.getId(), FullNameLDAPStorageMapperFactory.PROVIDER_ID, LDAPStorageMapper.class.getName(),
FullNameLDAPStorageMapper.LDAP_FULL_NAME_ATTRIBUTE, ldapFirstNameAttributeName,
FullNameLDAPStorageMapper.READ_ONLY, "false");
appRealm.addComponentModel(fullNameMapperModel);
} finally {
keycloakRule.stopSession(session, true);
}
session = keycloakRule.startSession();
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
// Assert user is successfully imported in Keycloak DB now with correct firstName and lastName
LDAPTestUtils.assertUserImported(session.users(), appRealm, "fullname", "James", "Dee", "fullname@email.org", "4578");
// change mapper to writeOnly
ComponentModel fullNameMapperModel = LDAPTestUtils.getSubcomponentByName(appRealm, ldapModel, "full name");
fullNameMapperModel.getConfig().putSingle(FullNameLDAPStorageMapper.WRITE_ONLY, "true");
appRealm.updateComponent(fullNameMapperModel);
} finally {
keycloakRule.stopSession(session, true);
}
// Assert changing user in Keycloak will change him in LDAP too...
session = keycloakRule.startSession();
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
UserModel fullnameUser = session.users().getUserByUsername("fullname", appRealm);
fullnameUser.setFirstName("James2");
fullnameUser.setLastName("Dee2");
} finally {
keycloakRule.stopSession(session, true);
}
// Assert changed user available in Keycloak
session = keycloakRule.startSession();
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
// Assert user is successfully imported in Keycloak DB now with correct firstName and lastName
LDAPTestUtils.assertUserImported(session.users(), appRealm, "fullname", "James2", "Dee2", "fullname@email.org", "4578");
// Remove "fullnameUser" to assert he is removed from LDAP. Revert mappers to previous state
UserModel fullnameUser = session.users().getUserByUsername("fullname", appRealm);
session.users().removeUser(appRealm, fullnameUser);
// Revert mappers
ComponentModel fullNameMapperModel = LDAPTestUtils.getSubcomponentByName(appRealm, ldapModel, "full name");
appRealm.removeComponent(fullNameMapperModel);
firstNameMapper.setId(null);
appRealm.addComponentModel(firstNameMapper);
} finally {
keycloakRule.stopSession(session, true);
}
}
@Test
public void testHardcodedAttributeMapperTest() throws Exception {
// Create hardcoded mapper for "description"
KeycloakSession session = keycloakRule.startSession();
ComponentModel hardcodedMapperModel = null;
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
hardcodedMapperModel = KeycloakModelUtils.createComponentModel("hardcodedAttr-description", ldapModel.getId(), HardcodedLDAPAttributeMapperFactory.PROVIDER_ID, LDAPStorageMapper.class.getName(),
HardcodedLDAPAttributeMapper.LDAP_ATTRIBUTE_NAME, "description",
HardcodedLDAPAttributeMapper.LDAP_ATTRIBUTE_VALUE, "some-${RANDOM}");
appRealm.addComponentModel(hardcodedMapperModel);
} finally {
keycloakRule.stopSession(session, true);
}
// Register new user
loginPage.open();
loginPage.clickRegister();
registerPage.assertCurrent();
registerPage.register("firstName", "lastName", "email34@check.cz", "register123", "Password1", "Password1");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
session = keycloakRule.startSession();
ComponentModel userAttrMapper = null;
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
// See that user don't yet have any description
UserModel user = LDAPTestUtils.assertUserImported(session.users(), appRealm, "register123", "firstName", "lastName", "email34@check.cz", null);
Assert.assertNull(user.getFirstAttribute("desc"));
Assert.assertNull(user.getFirstAttribute("description"));
// Remove hardcoded mapper for "description" and create regular userAttribute mapper for description
appRealm.removeComponent(hardcodedMapperModel);
userAttrMapper = LDAPTestUtils.addUserAttributeMapper(appRealm, ldapModel, "desc-attribute-mapper", "desc", "description");
userAttrMapper.put(UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "true");
appRealm.updateComponent(userAttrMapper);
} finally {
keycloakRule.stopSession(session, true);
}
// Check that user has description on him now
session = keycloakRule.startSession();
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
session.userCache().evict(appRealm, session.users().getUserByUsername("register123", appRealm));
// See that user don't yet have any description
UserModel user = session.users().getUserByUsername("register123", appRealm);
Assert.assertNull(user.getFirstAttribute("description"));
Assert.assertNotNull(user.getFirstAttribute("desc"));
String desc = user.getFirstAttribute("desc");
Assert.assertTrue(desc.startsWith("some-"));
Assert.assertEquals(35, desc.length());
// Remove mapper for "description"
appRealm.removeComponent(userAttrMapper);
} finally {
keycloakRule.stopSession(session, true);
}
}
@Test
public void testHardcodedRoleMapper() {
KeycloakSession session = keycloakRule.startSession();
ComponentModel firstNameMapper = null;
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
RoleModel hardcodedRole = appRealm.addRole("hardcoded-role");
// assert that user "johnkeycloak" doesn't have hardcoded role
UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
Assert.assertFalse(john.hasRole(hardcodedRole));
ComponentModel hardcodedMapperModel = KeycloakModelUtils.createComponentModel("hardcoded role", ldapModel.getId(), HardcodedLDAPRoleStorageMapperFactory.PROVIDER_ID, LDAPStorageMapper.class.getName(),
HardcodedLDAPRoleStorageMapper.ROLE, "hardcoded-role");
appRealm.addComponentModel(hardcodedMapperModel);
} finally {
keycloakRule.stopSession(session, true);
}
session = keycloakRule.startSession();
try {
RealmModel appRealm = new RealmManager(session).getRealmByName("test");
RoleModel hardcodedRole = appRealm.getRole("hardcoded-role");
// Assert user is successfully imported in Keycloak DB now with correct firstName and lastName
UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
Assert.assertTrue(john.hasRole(hardcodedRole));
// Can't remove user from hardcoded role
try {
john.deleteRoleMapping(hardcodedRole);
Assert.fail("Didn't expected to remove role mapping");
} catch (ModelException expected) {
}
// Revert mappers
ComponentModel hardcodedMapperModel = LDAPTestUtils.getSubcomponentByName(appRealm, ldapModel, "hardcoded role");
appRealm.removeComponent(hardcodedMapperModel);
} finally {
keycloakRule.stopSession(session, true);
}
}
@Test
public void testImportExistingUserFromLDAP() throws Exception {
// Add LDAP user with same email like existing model user
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "marykeycloak", "Mary1", "Kelly1", "mary1@email.org", null, "123");
LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "mary-duplicatemail", "Mary2", "Kelly2", "mary@test.com", null, "123");
LDAPObject marynoemail = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "marynoemail", "Mary1", "Kelly1", null, null, "123");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, marynoemail, "Password1");
}
});
// Try to import the duplicated LDAP user into Keycloak
loginPage.open();
loginPage.login("mary-duplicatemail", "password");
Assert.assertEquals("Email already exists.", loginPage.getError());
loginPage.login("mary1@email.org", "password");
Assert.assertEquals("Username already exists.", loginPage.getError());
loginSuccessAndLogout("marynoemail", "Password1");
}
@Test
public void testReadonly() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
UserStorageProviderModel model = new UserStorageProviderModel(ldapModel);
model.getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.READ_ONLY.toString());
appRealm.updateComponent(model);
UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm);
Assert.assertNotNull(user);
Assert.assertNotNull(user.getFederationLink());
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
try {
user.setEmail("error@error.com");
Assert.fail("should fail");
} catch (ReadOnlyException e) {
}
try {
user.setLastName("Berk");
Assert.fail("should fail");
} catch (ReadOnlyException e) {
}
try {
user.setFirstName("Bilbo");
Assert.fail("should fail");
} catch (ReadOnlyException e) {
}
try {
UserCredentialModel cred = UserCredentialModel.password("PoopyPoop1", true);
session.userCredentialManager().updateCredential(appRealm, user, cred);
Assert.fail("should fail");
} catch (ReadOnlyException e) {
}
Assert.assertTrue(session.users().removeUser(appRealm, user));
} finally {
keycloakRule.stopSession(session, false);
}
session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
Assert.assertEquals(UserStorageProvider.EditMode.WRITABLE.toString(), appRealm.getComponent(ldapModel.getId()).getConfig().getFirst(LDAPConstants.EDIT_MODE));
} finally {
keycloakRule.stopSession(session, false);
}
}
@Test
public void testRemoveFederatedUser() {
/*
{
KeycloakSession session = keycloakRule.startSession();
RealmModel appRealm = session.realms().getRealmByName("test");
UserModel user = session.users().getUserByUsername("registerUserSuccess2", appRealm);
keycloakRule.stopSession(session, true);
if (user == null) {
registerUserLdapSuccess();
}
}
*/
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
UserModel user = session.users().getUserByUsername("registerUserSuccess2", appRealm);
Assert.assertNotNull(user);
Assert.assertNotNull(user.getFederationLink());
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
Assert.assertTrue(session.users().removeUser(appRealm, user));
Assert.assertNull(session.users().getUserByUsername("registerUserSuccess2", appRealm));
} finally {
keycloakRule.stopSession(session, true);
}
}
@Test
public void testSearch() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "username1", "John1", "Doel1", "user1@email.org", null, "121");
LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "username2", "John2", "Doel2", "user2@email.org", null, "122");
LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "username3", "John3", "Doel3", "user3@email.org", null, "123");
LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "username4", "John4", "Doel4", "user4@email.org", null, "124");
// Users are not at local store at this moment
Assert.assertNull(session.userLocalStorage().getUserByUsername("username1", appRealm));
Assert.assertNull(session.userLocalStorage().getUserByUsername("username2", appRealm));
Assert.assertNull(session.userLocalStorage().getUserByUsername("username3", appRealm));
Assert.assertNull(session.userLocalStorage().getUserByUsername("username4", appRealm));
// search by username
session.users().searchForUser("username1", appRealm);
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username1", "John1", "Doel1", "user1@email.org", "121");
// search by email
session.users().searchForUser("user2@email.org", appRealm);
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username2", "John2", "Doel2", "user2@email.org", "122");
// search by lastName
session.users().searchForUser("Doel3", appRealm);
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username3", "John3", "Doel3", "user3@email.org", "123");
// search by firstName + lastName
session.users().searchForUser("John4 Doel4", appRealm);
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username4", "John4", "Doel4", "user4@email.org", "124");
} finally {
keycloakRule.stopSession(session, true);
}
}
@Test
public void testSearchWithCustomLDAPFilter() {
// Add custom filter for searching users
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
ldapModel.getConfig().putSingle(LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(|(mail=user5@email.org)(mail=user6@email.org))");
appRealm.updateComponent(ldapModel);
} finally {
keycloakRule.stopSession(session, true);
}
session = keycloakRule.startSession();
try {
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
RealmModel appRealm = session.realms().getRealmByName("test");
LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "username5", "John5", "Doel5", "user5@email.org", null, "125");
LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "username6", "John6", "Doel6", "user6@email.org", null, "126");
LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "username7", "John7", "Doel7", "user7@email.org", null, "127");
// search by email
List<UserModel> list = session.users().searchForUser("user5@email.org", appRealm);
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username5", "John5", "Doel5", "user5@email.org", "125");
session.users().searchForUser("John6 Doel6", appRealm);
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username6", "John6", "Doel6", "user6@email.org", "126");
session.users().searchForUser("user7@email.org", appRealm);
session.users().searchForUser("John7 Doel7", appRealm);
Assert.assertNull(session.userLocalStorage().getUserByUsername("username7", appRealm));
// Remove custom filter
ldapModel.getConfig().remove(LDAPConstants.CUSTOM_USER_SEARCH_FILTER);
appRealm.updateComponent(ldapModel);
} finally {
keycloakRule.stopSession(session, true);
}
}
@Test
public void testUnsynced() throws Exception {
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
UserStorageProviderModel model = new UserStorageProviderModel(ldapModel);
model.getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.UNSYNCED.toString());
appRealm.updateComponent(model);
UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm);
Assert.assertNotNull(user);
Assert.assertNotNull(user.getFederationLink());
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
UserCredentialModel cred = UserCredentialModel.password("Candycand1", true);
session.userCredentialManager().updateCredential(appRealm, user, cred);
CredentialModel userCredentialValueModel = session.userCredentialManager().getStoredCredentialsByType(appRealm, user, CredentialModel.PASSWORD).get(0);
Assert.assertEquals(UserCredentialModel.PASSWORD, userCredentialValueModel.getType());
Assert.assertTrue(session.userCredentialManager().isValid(appRealm, user, cred));
// LDAP password is still unchanged
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, model);
LDAPObject ldapUser = ldapProvider.loadLDAPUserByUsername(appRealm, "johnkeycloak");
ldapProvider.getLdapIdentityStore().validatePassword(ldapUser, "Password1");
// User is deleted just locally
Assert.assertTrue(session.users().removeUser(appRealm, user));
// Assert user not available locally, but will be reimported from LDAP once searched
Assert.assertNull(session.userLocalStorage().getUserByUsername("johnkeycloak", appRealm));
Assert.assertNotNull(session.users().getUserByUsername("johnkeycloak", appRealm));
} finally {
keycloakRule.stopSession(session, false);
}
session = keycloakRule.startSession();
try {
RealmModel appRealm = session.realms().getRealmByName("test");
Assert.assertEquals(UserStorageProvider.EditMode.WRITABLE.toString(), appRealm.getComponent(ldapModel.getId()).getConfig().getFirst(LDAPConstants.EDIT_MODE));
} finally {
keycloakRule.stopSession(session, false);
}
}
}