diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/KerberosLdapTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/KerberosLdapTest.java new file mode 100755 index 0000000000..385d1236f7 --- /dev/null +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/KerberosLdapTest.java @@ -0,0 +1,231 @@ +/* + * 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.junit.Assert; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.rules.TestRule; +import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.component.ComponentModel; +import org.keycloak.events.Details; +import org.keycloak.federation.kerberos.CommonKerberosConfig; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.LDAPConstants; +import org.keycloak.models.UserFederationProvider; +import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.ldap.LDAPStorageProviderFactory; +import org.keycloak.storage.ldap.kerberos.LDAPProviderKerberosConfig; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.federation.AbstractKerberosTest; +import org.keycloak.testsuite.federation.KerberosCredDelegServlet; +import org.keycloak.testsuite.rule.KerberosRule; +import org.keycloak.testsuite.rule.KeycloakRule; +import org.keycloak.testsuite.rule.WebRule; +import org.keycloak.utils.CredentialHelper; + +import javax.ws.rs.core.Response; +import java.net.URL; +import java.util.Map; + +/** + * Test of LDAPFederationProvider (Kerberos backed by LDAP) + * + * @author Marek Posolda + */ +public class KerberosLdapTest extends AbstractKerberosTest { + + private static final String PROVIDER_CONFIG_LOCATION = "kerberos/kerberos-ldap-connection.properties"; + + private static UserStorageProviderModel ldapModel = null; + + private static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION); + + private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() { + + @Override + public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { + CredentialHelper.setAlternativeCredential(manager.getSession(), CredentialRepresentation.KERBEROS, appRealm); + URL url = getClass().getResource("/kerberos-test/kerberos-app-keycloak.json"); + keycloakRule.createApplicationDeployment() + .name("kerberos-portal").contextPath("/kerberos-portal") + .servletClass(KerberosCredDelegServlet.class).adapterConfigPath(url.getPath()) + .role("user").deployApplication(); + + MultivaluedHashMap ldapConfig = LDAPTestUtils.toLdapConfig(kerberosRule.getConfig()); + 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 = new UserStorageProviderModel(appRealm.addComponentModel(model)); + } + }) { + + @Override + protected void importRealm() { + server.importRealm(getClass().getResourceAsStream("/kerberos-test/kerberosrealm.json")); + } + + }; + + @ClassRule + public static TestRule chain = RuleChain + .outerRule(kerberosRule) + .around(keycloakRule); + + @Rule + public WebRule webRule = new WebRule(this); + + @Rule + public AssertEvents events = new AssertEvents(keycloakRule); + + @Override + protected CommonKerberosConfig getKerberosConfig() { + return new LDAPProviderKerberosConfig(ldapModel); + } + + @Override + protected KeycloakRule getKeycloakRule() { + return keycloakRule; + } + + @Override + protected AssertEvents getAssertEvents() { + return events; + } + + + @Test + public void spnegoLoginTest() throws Exception { + spnegoLoginTestImpl(); + + // Assert user was imported and hasn't any required action on him. Profile info is synced from LDAP + assertUser("hnelson", "hnelson@keycloak.org", "Horatio", "Nelson", false); + } + + @Test + @Override + public void usernamePasswordLoginTest() throws Exception { + super.usernamePasswordLoginTest(); + } + + protected void updateProviderEditMode(LDAPStorageProviderFactory.EditMode editMode) { + KeycloakRule keycloakRule = getKeycloakRule(); + + KeycloakSession session = keycloakRule.startSession(); + try { + RealmModel realm = session.realms().getRealm("test"); + ComponentModel kerberosProviderModel = realm.getComponents(realm.getId(), UserStorageProvider.class.getName()).get(0); + kerberosProviderModel.getConfig().putSingle(LDAPConstants.EDIT_MODE, editMode.toString()); + realm.updateComponent(kerberosProviderModel); + } finally { + keycloakRule.stopSession(session, true); + } + } + + @Override + protected void updateProviderEditMode(UserFederationProvider.EditMode editMode) { + switch (editMode) { + case WRITABLE: + updateProviderEditMode(LDAPStorageProviderFactory.EditMode.WRITABLE); + break; + case READ_ONLY: + updateProviderEditMode(LDAPStorageProviderFactory.EditMode.READ_ONLY); + break; + case UNSYNCED: + updateProviderEditMode(LDAPStorageProviderFactory.EditMode.UNSYNCED); + break; + } + } + + @Test + public void writableEditModeTest() throws Exception { + KeycloakRule keycloakRule = getKeycloakRule(); + AssertEvents events = getAssertEvents(); + + // Change editMode to WRITABLE + updateProviderEditMode(LDAPStorageProviderFactory.EditMode.WRITABLE); + + // Login with username/password from kerberos + changePasswordPage.open(); + // Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript + // to forward the user if kerberos isn't enabled. + //bypassPage.isCurrent(); + //bypassPage.clickContinue(); + loginPage.assertCurrent(); + loginPage.login("jduke", "theduke"); + changePasswordPage.assertCurrent(); + + // Successfully change password now + changePasswordPage.changePassword("theduke", "newPass", "newPass"); + Assert.assertTrue(driver.getPageSource().contains("Your password has been updated.")); + changePasswordPage.logout(); + + // Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript + // to forward the user if kerberos isn't enabled. + //bypassPage.isCurrent(); + //bypassPage.clickContinue(); + + // Login with old password doesn't work, but with new password works + loginPage.login("jduke", "theduke"); + loginPage.assertCurrent(); + loginPage.login("jduke", "newPass"); + changePasswordPage.assertCurrent(); + changePasswordPage.logout(); + + // Assert SPNEGO login with the new password as mode is writable + events.clear(); + Response spnegoResponse = spnegoLogin("jduke", "newPass"); + Assert.assertEquals(302, spnegoResponse.getStatus()); + events.expectLogin() + .client("kerberos-app") + .user(keycloakRule.getUser("test", "jduke").getId()) + .detail(Details.REDIRECT_URI, KERBEROS_APP_URL) + //.detail(Details.AUTH_METHOD, "spnego") + .detail(Details.USERNAME, "jduke") + .assertEvent(); + + // Change password back + changePasswordPage.open(); + // Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript + // to forward the user if kerberos isn't enabled. + //bypassPage.isCurrent(); + //bypassPage.clickContinue(); + + loginPage.login("jduke", "newPass"); + changePasswordPage.assertCurrent(); + changePasswordPage.changePassword("newPass", "theduke", "theduke"); + Assert.assertTrue(driver.getPageSource().contains("Your password has been updated.")); + changePasswordPage.logout(); + + spnegoResponse.close(); + events.clear(); + } + +} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPTestUtils.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPTestUtils.java index 4b612f2f26..44a3f245db 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPTestUtils.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPTestUtils.java @@ -61,14 +61,18 @@ import java.util.Set; */ public class LDAPTestUtils { public static MultivaluedHashMap getLdapRuleConfig(LDAPRule ldapRule) { - MultivaluedHashMap config = new MultivaluedHashMap<>(); Map ldapConfig = ldapRule.getConfig(); + return toLdapConfig(ldapConfig); + + } + + public static MultivaluedHashMap toLdapConfig(Map ldapConfig) { + MultivaluedHashMap config = new MultivaluedHashMap<>(); for (Map.Entry entry : ldapConfig.entrySet()) { config.add(entry.getKey(), entry.getValue()); } return config; - }