diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/KerberosPortal.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/KerberosPortal.java new file mode 100644 index 0000000000..931ae024eb --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/KerberosPortal.java @@ -0,0 +1,43 @@ +/* + * 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.adapter.page; + +import java.net.URL; + +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl; + +/** + * + * @author pdrozd + */ +public class KerberosPortal extends AbstractPageWithInjectedUrl { + + public static final String DEPLOYMENT_NAME = "kerberos-portal"; + + @ArquillianResource + @OperateOnDeployment(DEPLOYMENT_NAME) + private URL url; + + @Override + public URL getInjectedUrl() { + return url; + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/KerberosCredDelegServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/KerberosCredDelegServlet.java new file mode 100644 index 0000000000..334591c6c7 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/KerberosCredDelegServlet.java @@ -0,0 +1,113 @@ +/* + * 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.adapter.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.security.sasl.Sasl; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.ietf.jgss.GSSCredential; +import org.keycloak.KeycloakPrincipal; +import org.keycloak.common.constants.KerberosConstants; +import org.keycloak.common.util.KerberosSerializationUtils; + +/** + * @author Marek Posolda + */ +public class KerberosCredDelegServlet extends HttpServlet { + + public static final String CRED_DELEG_TEST_PATH = "/cred-deleg-test"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String ldapData = null; + + if (req.getRequestURI().endsWith(CRED_DELEG_TEST_PATH)) { + + try { + // Retrieve kerberos credential from accessToken and deserialize it + KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) req.getUserPrincipal(); + String serializedGssCredential = (String) keycloakPrincipal.getKeycloakSecurityContext().getToken().getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL); + GSSCredential gssCredential = KerberosSerializationUtils.deserializeCredential(serializedGssCredential); + + // First try to invoke without gssCredential. It should fail + try { + invokeLdap(null); + throw new RuntimeException("Not expected to authenticate to LDAP without credential"); + } catch (NamingException nse) { + System.out.println("Expected exception: " + nse.getMessage()); + } + + ldapData = invokeLdap(gssCredential); + } catch (KerberosSerializationUtils.KerberosSerializationException kse) { + System.err.println("KerberosSerializationUtils.KerberosSerializationException: " + kse.getMessage()); + ldapData = "ERROR"; + } catch (Exception e) { + e.printStackTrace(); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + resp.setContentType("text/html"); + PrintWriter pw = resp.getWriter(); + pw.printf("%s", "Kerberos Test"); + pw.printf("Kerberos servlet secured content
"); + + if (ldapData != null) { + pw.printf("LDAP Data: " + ldapData + "
"); + } + + pw.print(""); + pw.flush(); + + + } + + private String invokeLdap(GSSCredential gssCredential) throws NamingException { + Hashtable env = new Hashtable(11); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, "ldap://localhost:10389"); + + if (gssCredential != null) { + env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI"); + env.put(Sasl.CREDENTIALS, gssCredential); + } + + DirContext ctx = new InitialDirContext(env); + try { + Attributes attrs = ctx.getAttributes("uid=hnelson,ou=People,dc=keycloak,dc=org"); + String cn = (String) attrs.get("cn").get(); + String sn = (String) attrs.get("sn").get(); + return cn + " " + sn; + } finally { + ctx.close(); + } + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java index 3938aa4491..f4a5d50dff 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java @@ -17,18 +17,19 @@ package org.keycloak.testsuite.util; +import static org.keycloak.testsuite.util.IOUtil.PROJECT_BUILD_DIRECTORY; + +import java.io.File; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.configuration.PropertiesConfiguration; import org.jboss.logging.Logger; import org.keycloak.common.constants.KerberosConstants; import org.keycloak.models.LDAPConstants; import org.keycloak.models.UserFederationProvider; -import java.io.File; -import java.io.InputStream; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - /** * @author Marek Posolda */ @@ -37,7 +38,8 @@ public class LDAPTestConfiguration { private static final Logger log = Logger.getLogger(LDAPTestConfiguration.class); private String connectionPropertiesLocation; - private boolean startEmbeddedLdapLerver = true; + private int sleepTime; + private boolean startEmbeddedLdapServer = true; private Map config; protected static final Map PROP_MAPPINGS = new HashMap(); @@ -83,8 +85,7 @@ public class LDAPTestConfiguration { DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false"); DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG"); DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG"); - URL keytabUrl = LDAPTestConfiguration.class.getResource("/kerberos/http.keytab"); - String keyTabPath = new File(keytabUrl.getFile()).getAbsolutePath(); + String keyTabPath = getResource("http.keytab"); DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath); DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true"); DEFAULT_VALUES.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "true"); @@ -94,17 +95,22 @@ public class LDAPTestConfiguration { public static LDAPTestConfiguration readConfiguration(String connectionPropertiesLocation) { LDAPTestConfiguration ldapTestConfiguration = new LDAPTestConfiguration(); - ldapTestConfiguration.setConnectionPropertiesLocation(connectionPropertiesLocation); + ldapTestConfiguration.setConnectionPropertiesLocation(getResource(connectionPropertiesLocation)); ldapTestConfiguration.loadConnectionProperties(); return ldapTestConfiguration; } + + public static String getResource(String resourceName) { + return new File(PROJECT_BUILD_DIRECTORY, "dependency/kerberos/" + resourceName).getAbsolutePath(); + } protected void loadConnectionProperties() { - Properties p = new Properties(); + PropertiesConfiguration p; try { log.info("Reading LDAP configuration from: " + connectionPropertiesLocation); - InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(connectionPropertiesLocation); - p.load(is); + p = new PropertiesConfiguration(); + p.setDelimiterParsingDisabled(true); + p.load(connectionPropertiesLocation); } catch (Exception e) { throw new RuntimeException(e); @@ -115,7 +121,7 @@ public class LDAPTestConfiguration { String propertyName = property.getKey(); String configName = property.getValue(); - String value = (String) p.get(configName); + String value = p.getString(configName); if (value == null) { value = DEFAULT_VALUES.get(propertyName); } @@ -123,8 +129,9 @@ public class LDAPTestConfiguration { config.put(propertyName, value); } - startEmbeddedLdapLerver = Boolean.parseBoolean(p.getProperty("idm.test.ldap.start.embedded.ldap.server", "true")); - log.info("Start embedded server: " + startEmbeddedLdapLerver); + startEmbeddedLdapServer = p.getBoolean("idm.test.ldap.start.embedded.ldap.server", true); + sleepTime = p.getInteger("idm.test.ldap.sleepTime", 1000); + log.info("Start embedded server: " + startEmbeddedLdapServer); log.info("Read config: " + config); } @@ -136,8 +143,12 @@ public class LDAPTestConfiguration { this.connectionPropertiesLocation = connectionPropertiesLocation; } - public boolean isStartEmbeddedLdapLerver() { - return startEmbeddedLdapLerver; + public boolean isStartEmbeddedLdapServer() { + return startEmbeddedLdapServer; + } + + public int getSleepTime() { + return sleepTime; } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java index f66fdf8ec1..6df6511a5b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java @@ -52,10 +52,10 @@ import java.util.Map; */ public class AssertEvents { - static final String DEFAULT_CLIENT_ID = "test-app"; - static final String DEFAULT_IP_ADDRESS = "127.0.0.1"; - static final String DEFAULT_REALM = "test"; - static final String DEFAULT_USERNAME = "test-user@localhost"; + public static final String DEFAULT_CLIENT_ID = "test-app"; + public static final String DEFAULT_IP_ADDRESS = "127.0.0.1"; + public static final String DEFAULT_REALM = "test"; + public static final String DEFAULT_USERNAME = "test-user@localhost"; String defaultRedirectUri = "http://localhost:8180/auth/realms/master/app/auth"; String defaultEventsQueueUri = "http://localhost:8092"; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/AbstractKerberosAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/AbstractKerberosAdapterTest.java new file mode 100755 index 0000000000..0bd5b01771 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/AbstractKerberosAdapterTest.java @@ -0,0 +1,366 @@ +/* + * 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.adapter.federation; + +import static org.keycloak.testsuite.util.IOUtil.loadRealm; + +import java.security.Principal; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; + +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.client.params.AuthPolicy; +import org.apache.http.impl.client.DefaultHttpClient; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.graphene.page.Page; +import org.jboss.resteasy.client.jaxrs.ResteasyClient; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.adapters.HttpClientBuilder; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.common.constants.KerberosConstants; +import org.keycloak.events.Details; +import org.keycloak.federation.kerberos.CommonKerberosConfig; +import org.keycloak.models.LDAPConstants; +import org.keycloak.models.UserFederationProvider; +import org.keycloak.models.UserFederationProviderModel; +import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserFederationProviderRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest; +import org.keycloak.testsuite.adapter.page.KerberosPortal; +import org.keycloak.testsuite.adapter.servlet.KerberosCredDelegServlet; +import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.auth.page.AuthRealm; +import org.keycloak.testsuite.auth.page.account.ChangePassword; +import org.keycloak.testsuite.util.LDAPTestConfiguration; +import org.keycloak.util.ldap.KerberosEmbeddedServer; +import org.keycloak.util.ldap.LDAPEmbeddedServer; + +/** + * @author Marek Posolda + */ +public abstract class AbstractKerberosAdapterTest extends AbstractServletsAdapterTest { + + protected static LDAPTestConfiguration ldapTestConfiguration; + + protected KeycloakSPNegoSchemeFactory spnegoSchemeFactory; + + protected ResteasyClient client; + + protected static LDAPEmbeddedServer ldapEmbeddedServer; + + protected AssertEvents events; + + @Page + protected ChangePassword changePasswordPage; + + @Page + protected KerberosPortal kerberosPortal; + + protected abstract String getConnectionPropertiesLocation(); + + protected abstract CommonKerberosConfig getKerberosConfig(UserFederationProviderModel model); + + @Deployment(name = KerberosPortal.DEPLOYMENT_NAME) + protected static WebArchive kerberosPortal() { + return servletDeployment(KerberosPortal.DEPLOYMENT_NAME, "keycloak.json", KerberosCredDelegServlet.class); + } + + @Override + public void addAdapterTestRealms(List testRealms) { + testRealms.add(loadRealm("/adapter-test/kerberosrealm.json")); + } + + + @Before + public void before() throws Exception { + testRealmPage.setAuthRealm(AuthRealm.TEST); + changePasswordPage.setAuthRealm(testRealmPage); + ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(getConnectionPropertiesLocation()); + if (ldapTestConfiguration.isStartEmbeddedLdapServer() && ldapEmbeddedServer == null) { + ldapEmbeddedServer = createServer(); + ldapEmbeddedServer.init(); + ldapEmbeddedServer.start(); + } + // Global kerberos configuration + String krb5ConfPath = LDAPTestConfiguration.getResource("test-krb5.conf"); + log.info("Krb5.conf file location is: " + krb5ConfPath); + System.setProperty("java.security.krb5.conf", krb5ConfPath); + events = new AssertEvents(this); + UserFederationProviderModel model = new UserFederationProviderModel(); + model.setConfig(ldapTestConfiguration.getLDAPConfig()); + spnegoSchemeFactory = new KeycloakSPNegoSchemeFactory(getKerberosConfig(model)); + initHttpClient(true); + removeAllUsers(); + } + + @After + public void after() { + client.close(); + client = null; + } + + @AfterClass + public static void afterClass() { + try { + if (ldapEmbeddedServer != null) { + ldapEmbeddedServer.stop(); + ldapEmbeddedServer = null; + ldapTestConfiguration = null; + } + } catch (Exception e) { + throw new RuntimeException("Error tearDown Embedded LDAP server.", e); + } + } + + @Test + public void spnegoNotAvailableTest() throws Exception { + initHttpClient(false); + + String kcLoginPageLocation = client.target(kerberosPortal.getInjectedUrl().toString()).request().get().getLocation().toString(); + + Response response = client.target(kcLoginPageLocation).request().get(); + Assert.assertEquals(401, response.getStatus()); + Assert.assertEquals(KerberosConstants.NEGOTIATE, response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE)); + String responseText = response.readEntity(String.class); + responseText.contains("Log in to test"); + response.close(); + } + + protected void spnegoLoginTestImpl() throws Exception { + Response spnegoResponse = spnegoLogin("hnelson", "secret"); + Assert.assertEquals(302, spnegoResponse.getStatus()); + + List users = testRealmResource().users().search("hnelson", 0, 1); + String userId = users.get(0).getId(); + events.expectLogin() + .client("kerberos-app") + .user(userId) + .detail(Details.REDIRECT_URI, kerberosPortal.toString()) + //.detail(Details.AUTH_METHOD, "spnego") + .detail(Details.USERNAME, "hnelson") + .assertEvent(); + + String location = spnegoResponse.getLocation().toString(); + driver.navigate().to(location); + + String pageSource = driver.getPageSource(); + Assert.assertTrue( + pageSource.contains("Kerberos Test") && pageSource.contains("Kerberos servlet secured content")); + + spnegoResponse.close(); + events.clear(); + } + + // KEYCLOAK-2102 + @Test + public void spnegoCaseInsensitiveTest() throws Exception { + Response spnegoResponse = spnegoLogin("MyDuke", "theduke"); + Assert.assertEquals(302, spnegoResponse.getStatus()); + List users = testRealmResource().users().search("myduke", 0, 1); + String userId = users.get(0).getId(); + events.expectLogin() + .client("kerberos-app") + .user(userId) + .detail(Details.REDIRECT_URI, kerberosPortal.toString()) + //.detail(Details.AUTH_METHOD, "spnego") + .detail(Details.USERNAME, "myduke") + .assertEvent(); + + String location = spnegoResponse.getLocation().toString(); + driver.navigate().to(location); + + String pageSource = driver.getPageSource(); + Assert.assertTrue( + pageSource.contains("Kerberos Test") && pageSource.contains("Kerberos servlet secured content")); + + spnegoResponse.close(); + events.clear(); + } + + @Test + public void usernamePasswordLoginTest() throws Exception { + // Change editMode to READ_ONLY + updateProviderEditMode(UserFederationProvider.EditMode.READ_ONLY); + + // Login with username/password from kerberos + changePasswordPage.navigateTo(); + testRealmLoginPage.isCurrent(); + testRealmLoginPage.form().login("jduke", "theduke"); + changePasswordPage.isCurrent(); + + // Bad existing password + changePasswordPage.changePasswords("theduke-invalid", "newPass", "newPass"); + Assert.assertTrue(driver.getPageSource().contains("Invalid existing password.")); + + // Change password is not possible as editMode is READ_ONLY + changePasswordPage.changePasswords("theduke", "newPass", "newPass"); + Assert.assertTrue( + driver.getPageSource().contains("You can't update your password as your account is read only")); + + // Change editMode to UNSYNCED + updateProviderEditMode(UserFederationProvider.EditMode.UNSYNCED); + + // Successfully change password now + changePasswordPage.changePasswords("theduke", "newPass", "newPass"); + Assert.assertTrue(driver.getPageSource().contains("Your password has been updated.")); + changePasswordPage.logOut(); + + // Login with old password doesn't work, but with new password works + testRealmLoginPage.form().login("jduke", "theduke"); + testRealmLoginPage.isCurrent(); + testRealmLoginPage.form().login("jduke", "newPass"); + changePasswordPage.isCurrent(); + changePasswordPage.logOut(); + + // Assert SPNEGO login still with the old password as mode is unsynced + events.clear(); + Response spnegoResponse = spnegoLogin("jduke", "theduke"); + Assert.assertEquals(302, spnegoResponse.getStatus()); + UserRepresentation user = ApiUtil.findUserByUsername(testRealmResource(), "jduke"); + events.expectLogin() + .client("kerberos-app") + .user(user != null ? user.getId() : null) + .detail(Details.REDIRECT_URI, kerberosPortal.toString()) + //.detail(Details.AUTH_METHOD, "spnego") + .detail(Details.USERNAME, "jduke") + .assertEvent(); + spnegoResponse.close(); + } + + + protected Response spnegoLogin(String username, String password) { + kerberosPortal.navigateTo(); + Response res = client.target(kerberosPortal.getInjectedUrl().toString()).request().get(); + String kcLoginPageLocation = res.getLocation().toString(); + if (driver.manage().getCookieNamed("OAuth_Token_Request_State") != null) { + kcLoginPageLocation = res.getLocation().toString().replaceFirst("state=.*&", "state=" + driver.manage().getCookieNamed("OAuth_Token_Request_State").getValue() + "&"); + } + // Request for SPNEGO login sent with Resteasy client + spnegoSchemeFactory.setCredentials(username, password); + Response response = client.target(kcLoginPageLocation).request().get(); + if (response.getStatus() == 302) { + if (response.getLocation() == null) + return response; + String uri = response.getLocation().toString(); + if (uri.contains("login-actions/required-action")) { + response = client.target(uri).request().get(); + } + } + return response; + + } + + + + protected void initHttpClient(boolean useSpnego) { + if (client != null) { + after(); + } + DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build(); + httpClient.getAuthSchemes().register(AuthPolicy.SPNEGO, spnegoSchemeFactory); + + if (useSpnego) { + Credentials fake = new Credentials() { + + public String getPassword() { + return null; + } + + public Principal getUserPrincipal() { + return null; + } + + }; + + httpClient.getCredentialsProvider().setCredentials( + new AuthScope(null, -1, null), + fake); + } + ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient); + client = new ResteasyClientBuilder().httpEngine(engine).build(); + } + + protected void removeAllUsers() { + RealmResource realm = testRealmResource(); + List users = realm.users().search("", 0, Integer.MAX_VALUE); + for (UserRepresentation user : users) { + if (!user.getUsername().equals(AssertEvents.DEFAULT_USERNAME)) { + realm.users().get(user.getId()).remove(); + } + } + Assert.assertEquals(1, realm.users().search("", 0, Integer.MAX_VALUE).size()); + } + + protected void assertUser(String expectedUsername, String expectedEmail, String expectedFirstname, + String expectedLastname, boolean updateProfileActionExpected) { + try { + UserRepresentation user = ApiUtil.findUserByUsername(testRealmResource(), expectedUsername); + Assert.assertNotNull(user); + Assert.assertEquals(expectedEmail, user.getEmail()); + Assert.assertEquals(expectedFirstname, user.getFirstName()); + Assert.assertEquals(expectedLastname, user.getLastName()); + + if (updateProfileActionExpected) { + Assert.assertEquals(UserModel.RequiredAction.UPDATE_PROFILE.toString(), + user.getRequiredActions().iterator().next()); + } else { + Assert.assertTrue(user.getRequiredActions().isEmpty()); + } + } finally { + } + } + + protected void updateProviderEditMode(UserFederationProvider.EditMode editMode) { + RealmResource realm = testRealmResource(); + RealmRepresentation realmRepresentation = realm.toRepresentation(); + UserFederationProviderRepresentation kerberosProviderRepresentation = realmRepresentation + .getUserFederationProviders().get(0); + kerberosProviderRepresentation.getConfig().put(LDAPConstants.EDIT_MODE, editMode.toString()); + realm.update(realmRepresentation); + } + + public RealmResource testRealmResource() { + return adminClient.realm("test"); + } + + public Map getConfig() { + return ldapTestConfiguration.getLDAPConfig(); + } + + protected static LDAPEmbeddedServer createServer() { + Properties defaultProperties = new Properties(); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_DSF, LDAPEmbeddedServer.DSF_INMEMORY); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_LDIF_FILE, "classpath:kerberos/users-kerberos.ldif"); + return new KerberosEmbeddedServer(defaultProperties); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/AbstractKerberosLdapAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/AbstractKerberosLdapAdapterTest.java new file mode 100755 index 0000000000..127f90a4b3 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/AbstractKerberosLdapAdapterTest.java @@ -0,0 +1,130 @@ +/* + * 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.adapter.federation; + +import java.util.Arrays; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.events.Details; +import org.keycloak.federation.kerberos.CommonKerberosConfig; +import org.keycloak.federation.ldap.LDAPFederationProviderFactory; +import org.keycloak.federation.ldap.kerberos.LDAPProviderKerberosConfig; +import org.keycloak.models.UserFederationProvider; +import org.keycloak.models.UserFederationProviderModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserFederationProviderRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.admin.ApiUtil; + +/** + * Test of LDAPFederationProvider (Kerberos backed by LDAP) + * + * @author Marek Posolda + */ +public abstract class AbstractKerberosLdapAdapterTest extends AbstractKerberosAdapterTest { + + private static final String PROVIDER_CONFIG_LOCATION = "kerberos-ldap-connection.properties"; + + @Before + public void init() throws Exception{ + Map ldapConfig = getConfig(); + UserFederationProviderRepresentation userFederationProviderRepresentation = new UserFederationProviderRepresentation(); + userFederationProviderRepresentation.setProviderName(LDAPFederationProviderFactory.PROVIDER_NAME); + userFederationProviderRepresentation.setConfig(ldapConfig); + userFederationProviderRepresentation.setPriority(0); + userFederationProviderRepresentation.setDisplayName("kerberos-ldap"); + userFederationProviderRepresentation.setFullSyncPeriod(-1); + userFederationProviderRepresentation.setChangedSyncPeriod(-1); + userFederationProviderRepresentation.setLastSync(0); + RealmRepresentation realmRepresentation = testRealmResource().toRepresentation(); + realmRepresentation.setUserFederationProviders(Arrays.asList(userFederationProviderRepresentation)); + realmRepresentation.setEventsEnabled(true); + testRealmResource().update(realmRepresentation); + } + + @Override + protected CommonKerberosConfig getKerberosConfig(UserFederationProviderModel model) { + return new LDAPProviderKerberosConfig(model); + } + + @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 + public void writableEditModeTest() throws Exception { + + // Change editMode to WRITABLE + updateProviderEditMode(UserFederationProvider.EditMode.WRITABLE); + + // Login with username/password from kerberos + changePasswordPage.navigateTo(); + loginPage.isCurrent(); + loginPage.form().login("jduke", "theduke"); + changePasswordPage.isCurrent(); + + // Successfully change password now + changePasswordPage.changePasswords("theduke", "newPass", "newPass"); + Assert.assertTrue(driver.getPageSource().contains("Your password has been updated.")); + changePasswordPage.logOut(); + + // Login with old password doesn't work, but with new password works + loginPage.form().login("jduke", "theduke"); + loginPage.isCurrent(); + loginPage.form().login("jduke", "newPass"); + changePasswordPage.isCurrent(); + 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()); + UserRepresentation user = ApiUtil.findUserByUsername(testRealmResource(), "jduke"); + events.expectLogin() + .client("kerberos-app") + .user(user != null ? user.getId() : null) + .detail(Details.REDIRECT_URI, kerberosPortal.toString()) + //.detail(Details.AUTH_METHOD, "spnego") + .detail(Details.USERNAME, "jduke") + .assertEvent(); + + // Change password back + changePasswordPage.navigateTo();; + + loginPage.form().login("jduke", "newPass"); + changePasswordPage.isCurrent(); + changePasswordPage.changePasswords("newPass", "theduke", "theduke"); + Assert.assertTrue(driver.getPageSource().contains("Your password has been updated.")); + changePasswordPage.logOut(); + + spnegoResponse.close(); + events.clear(); + } + + protected String getConnectionPropertiesLocation() { + return PROVIDER_CONFIG_LOCATION; + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/AbstractKerberosStandaloneAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/AbstractKerberosStandaloneAdapterTest.java new file mode 100755 index 0000000000..864b008c18 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/AbstractKerberosStandaloneAdapterTest.java @@ -0,0 +1,113 @@ +/* + * 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.adapter.federation; + +import java.util.Arrays; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.common.constants.KerberosConstants; +import org.keycloak.federation.kerberos.CommonKerberosConfig; +import org.keycloak.federation.kerberos.KerberosConfig; +import org.keycloak.federation.kerberos.KerberosFederationProviderFactory; +import org.keycloak.models.UserFederationProviderModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserFederationProviderRepresentation; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxProfile; + +/** + * Test of KerberosFederationProvider (Kerberos not backed by LDAP) + * + * @author Marek Posolda + */ +public abstract class AbstractKerberosStandaloneAdapterTest extends AbstractKerberosAdapterTest { + + protected static final String PROVIDER_CONFIG_LOCATION = "kerberos-standalone-connection.properties"; + + @Before + public void init() throws Exception{ + Map ldapConfig = getConfig(); + UserFederationProviderRepresentation userFederationProviderRepresentation = new UserFederationProviderRepresentation(); + userFederationProviderRepresentation.setProviderName(KerberosFederationProviderFactory.PROVIDER_NAME); + userFederationProviderRepresentation.setConfig(ldapConfig); + userFederationProviderRepresentation.setPriority(0); + userFederationProviderRepresentation.setDisplayName("kerberos-standalone"); + userFederationProviderRepresentation.setFullSyncPeriod(-1); + userFederationProviderRepresentation.setChangedSyncPeriod(-1); + userFederationProviderRepresentation.setLastSync(0); + + RealmRepresentation realmRepresentation = testRealmResource().toRepresentation(); + realmRepresentation.setUserFederationProviders(Arrays.asList(userFederationProviderRepresentation)); + realmRepresentation.setEventsEnabled(true); + testRealmResource().update(realmRepresentation); + } + + @Override + protected CommonKerberosConfig getKerberosConfig(UserFederationProviderModel model) { + return new KerberosConfig(model); + } + + @Override + protected String getConnectionPropertiesLocation() { + return PROVIDER_CONFIG_LOCATION; + } + + @Test + public void spnegoLoginTest() throws Exception { + spnegoLoginTestImpl(); + // Assert user was imported and hasn't any required action on him + assertUser("hnelson", "hnelson@keycloak.org", null, null, false); + } + + @Test + @Override + public void usernamePasswordLoginTest() throws Exception { + super.usernamePasswordLoginTest(); + } + + @Test + public void updateProfileEnabledTest() throws Exception { + // Switch updateProfileOnFirstLogin to on + RealmRepresentation realm = testRealmResource().toRepresentation(); + UserFederationProviderRepresentation kerberosProviderRepresentation = realm.getUserFederationProviders().get(0); + kerberosProviderRepresentation.getConfig().put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true"); + testRealmResource().update(realm); + + // Assert update profile page is displayed + Response spnegoResponse = spnegoLogin("hnelson", "secret"); + Assert.assertEquals(200, spnegoResponse.getStatus()); + String responseText = spnegoResponse.readEntity(String.class); + Assert.assertTrue(responseText.contains("You need to update your user profile to activate your account.")); + Assert.assertTrue(responseText.contains("hnelson@keycloak.org")); + spnegoResponse.close(); + + // Assert user was imported and has required action on him + assertUser("hnelson", "hnelson@keycloak.org", null, null, true); + + // Switch updateProfileOnFirstLogin to off + + kerberosProviderRepresentation = realm.getUserFederationProviders().get(0); + kerberosProviderRepresentation.getConfig().put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "false"); + testRealmResource().update(realm); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/KeycloakSPNegoSchemeFactory.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/KeycloakSPNegoSchemeFactory.java new file mode 100644 index 0000000000..502bfe0b99 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/federation/KeycloakSPNegoSchemeFactory.java @@ -0,0 +1,131 @@ +/* + * 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.adapter.federation; + +import java.security.PrivilegedExceptionAction; + +import javax.security.auth.Subject; + +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.Credentials; +import org.apache.http.impl.auth.SPNegoScheme; +import org.apache.http.impl.auth.SPNegoSchemeFactory; +import org.apache.http.params.HttpParams; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; +import org.keycloak.federation.kerberos.CommonKerberosConfig; +import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator; + +/** + * Usable for testing only. Username and password are shared for the whole factory + * + * @author Marek Posolda + */ +public class KeycloakSPNegoSchemeFactory extends SPNegoSchemeFactory { + + private final CommonKerberosConfig kerberosConfig; + + private String username; + private String password; + + + public KeycloakSPNegoSchemeFactory(CommonKerberosConfig kerberosConfig) { + super(true, false); + this.kerberosConfig = kerberosConfig; + } + + + public void setCredentials(String username, String password) { + this.username = username; + this.password = password; + } + + + @Override + public AuthScheme newInstance(HttpParams params) { + return new KeycloakSPNegoScheme(isStripPort(), isUseCanonicalHostname()); + } + + + public class KeycloakSPNegoScheme extends SPNegoScheme { + + public KeycloakSPNegoScheme(boolean stripPort, boolean useCanonicalHostname) { + super(stripPort, useCanonicalHostname); + } + + + @Override + protected byte[] generateGSSToken(byte[] input, Oid oid, String authServer, Credentials credentials) throws GSSException { + KerberosUsernamePasswordAuthenticator authenticator = new KerberosUsernamePasswordAuthenticator(kerberosConfig); + try { + Subject clientSubject = authenticator.authenticateSubject(username, password); + + ByteArrayHolder holder = Subject.doAs(clientSubject, new ClientAcceptSecContext(input, oid, authServer)); + + return holder.bytes; + } catch (Exception le) { + throw new RuntimeException(le); + } finally { + authenticator.logoutSubject(); + } + } + + + private class ClientAcceptSecContext implements PrivilegedExceptionAction { + + private final byte[] input; + private final Oid oid; + private final String authServer; + + public ClientAcceptSecContext(byte[] input, Oid oid, String authServer) { + this.input = input; + this.oid = oid; + this.authServer = authServer; + } + + + @Override + public ByteArrayHolder run() throws Exception { + byte[] token = input; + if (token == null) { + token = new byte[0]; + } + GSSManager manager = getManager(); + GSSName serverName = manager.createName("HTTP/" + authServer + "@" + kerberosConfig.getKerberosRealm(), null); + GSSContext gssContext = manager.createContext( + serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME); + gssContext.requestMutualAuth(true); + gssContext.requestCredDeleg(true); + byte[] outputToken = gssContext.initSecContext(token, 0, token.length); + + ByteArrayHolder result = new ByteArrayHolder(); + result.bytes = outputToken; + return result; + } + + } + + + private class ByteArrayHolder { + private byte[] bytes; + } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/META-INF/context.xml new file mode 100644 index 0000000000..bb128d1c0a --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/META-INF/context.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/jetty-web.xml new file mode 100644 index 0000000000..64863de3ef --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/jetty-web.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/keycloak-relative.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/keycloak-relative.json new file mode 100644 index 0000000000..549fde40b7 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/keycloak-relative.json @@ -0,0 +1,9 @@ +{ + "realm" : "test", + "resource" : "kerberos-app", + "auth-server-url" : "/auth", + "ssl-required" : "external", + "credentials" : { + "secret": "password" + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/keycloak.json new file mode 100644 index 0000000000..609bcdb1aa --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/keycloak.json @@ -0,0 +1,10 @@ +{ + "realm": "test", + "resource": "kerberos-app", + "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "auth-server-url": "http://localhost:8081/auth", + "ssl-required" : "external", + "credentials": { + "secret": "password" + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/web.xml new file mode 100644 index 0000000000..a52c8e5260 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberos-portal/WEB-INF/web.xml @@ -0,0 +1,57 @@ + + + + + + kerberos-portal + + + Servlet + org.keycloak.testsuite.adapter.servlet.KerberosCredDelegServlet + + + + Servlet + /* + + + + + Users + /* + + + user + + + + + KEYCLOAK + test + + + + admin + + + user + + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberosrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberosrealm.json new file mode 100644 index 0000000000..a77d447771 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/kerberosrealm.json @@ -0,0 +1,54 @@ +{ + "id": "test", + "realm": "test", + "enabled": true, + "sslRequired": "external", + "registrationAllowed": true, + "resetPasswordAllowed": true, + "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", + "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "requiredCredentials": [ "password", "kerberos" ], + "defaultRoles": [ "user" ], + "users" : [ + { + "username" : "test-user@localhost", + "enabled": true, + "email" : "test-user@localhost", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ], + "realmRoles": ["user"], + "applicationRoles": { + "account": [ "view-profile", "manage-account" ] + } + } + ], + "scopeMappings": [ + { + "client": "kerberos-app", + "roles": ["user"] + } + ], + "clients": [ + { + "clientId": "kerberos-app", + "enabled": true, + "adminUrl": "/kerberos-portal/logout", + "baseUrl": "/kerberos-portal", + "redirectUris": [ + "/kerberos-portal/*" + ], + "secret": "password" + } + ], + "roles" : { + "realm" : [ + { + "name": "user", + "description": "Have User privileges" + } + ] + }, + "eventsListeners": ["jboss-logging", "event-queue"] +} \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/http.keytab b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/http.keytab new file mode 100644 index 0000000000..0e7fd96fa7 Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/http.keytab differ diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/kerberos-ldap-connection.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/kerberos-ldap-connection.properties new file mode 100644 index 0000000000..41876a48d2 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/kerberos-ldap-connection.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +idm.test.ldap.connection.url=ldap\://localhost\:10389 +idm.test.ldap.base.dn=dc\=keycloak,dc\=org +idm.test.ldap.roles.dn.suffix=ou\=Roles,dc\=keycloak,dc\=org +idm.test.ldap.group.dn.suffix=ou\=Groups,dc\=keycloak,dc\=org +idm.test.ldap.user.dn.suffix=ou\=People,dc\=keycloak,dc\=org +idm.test.ldap.start.embedded.ldap.server=true +idm.test.ldap.bind.dn=uid\=admin,ou\=system +idm.test.ldap.bind.credential=secret +idm.test.ldap.connection.pooling=true +idm.test.ldap.pagination=true +idm.test.ldap.batch.size.for.sync=3 + +idm.test.kerberos.allow.kerberos.authentication=true +idm.test.kerberos.realm=KEYCLOAK.ORG +idm.test.kerberos.server.principal=HTTP/localhost@KEYCLOAK.ORG +idm.test.kerberos.debug=true +idm.test.kerberos.use.kerberos.for.password.authentication=true \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/kerberos-standalone-connection.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/kerberos-standalone-connection.properties new file mode 100644 index 0000000000..38754a947a --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/kerberos-standalone-connection.properties @@ -0,0 +1,23 @@ +# +# 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. +# + +idm.test.kerberos.allow.kerberos.authentication=true +idm.test.kerberos.realm=KEYCLOAK.ORG +idm.test.kerberos.server.principal=HTTP/localhost@KEYCLOAK.ORG +idm.test.kerberos.debug=false +idm.test.kerberos.allow.password.authentication=true +idm.test.kerberos.update.profile.first.login=false \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/test-krb5.conf b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/test-krb5.conf new file mode 100644 index 0000000000..6989b7f5e6 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/test-krb5.conf @@ -0,0 +1,19 @@ +[libdefaults] + default_realm = KEYCLOAK.ORG + default_tgs_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac aes128-cts-hmac-sha1-96 + default_tkt_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac aes128-cts-hmac-sha1-96 + permitted_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac aes128-cts-hmac-sha1-96 + kdc_timeout = 30000 + dns_lookup_realm = false + dns_lookup_kdc = false + dns_canonicalize_hostname = false + ignore_acceptor_hostname = true + forwardable = true + +[realms] + KEYCLOAK.ORG = { + kdc = localhost:6088 + } + +[domain_realm] + localhost = KEYCLOAK.ORG \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/users-kerberos.ldif b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/users-kerberos.ldif new file mode 100644 index 0000000000..9d55092804 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/users-kerberos.ldif @@ -0,0 +1,104 @@ +dn: dc=keycloak,dc=org +objectclass: dcObject +objectclass: organization +o: Keycloak +dc: Keycloak + +dn: ou=People,dc=keycloak,dc=org +objectClass: organizationalUnit +objectClass: top +ou: People + +dn: uid=krbtgt,ou=People,dc=keycloak,dc=org +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: KDC Service +sn: Service +uid: krbtgt +userPassword: secret +krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KEYCLOAK.ORG +krb5KeyVersionNumber: 0 + +dn: uid=ldap,ou=People,dc=keycloak,dc=org +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: LDAP +sn: Service +uid: ldap +userPassword: randall +krb5PrincipalName: ${ldapSaslPrincipal} +krb5KeyVersionNumber: 0 + +dn: uid=HTTP,ou=People,dc=keycloak,dc=org +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: HTTP +sn: Service +uid: HTTP +userPassword: httppwd +krb5PrincipalName: HTTP/${hostname}@KEYCLOAK.ORG +krb5KeyVersionNumber: 0 + +dn: uid=hnelson,ou=People,dc=keycloak,dc=org +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: Horatio +sn: Nelson +mail: hnelson@keycloak.org +uid: hnelson +userPassword: secret +krb5PrincipalName: hnelson@KEYCLOAK.ORG +krb5KeyVersionNumber: 0 + +dn: uid=jduke,ou=People,dc=keycloak,dc=org +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: Java +sn: Duke +mail: jduke@keycloak.org +uid: jduke +userPassword: theduke +krb5PrincipalName: jduke@KEYCLOAK.ORG +krb5KeyVersionNumber: 0 + +dn: uid=MyDuke,ou=People,dc=keycloak,dc=org +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: My +sn: Duke +mail: MyDuke@keycloak.org +uid: MyDuke +userPassword: theduke +krb5PrincipalName: MyDuke@KEYCLOAK.ORG +krb5KeyVersionNumber: 0 + +dn: uid=gsstestserver,ou=People,dc=keycloak,dc=org +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: gsstestserver +sn: Service +uid: gsstestserver +userPassword: gsstestpwd +krb5PrincipalName: gsstestserver/xxx@KEYCLOAK.ORG +krb5KeyVersionNumber: 0 diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/EAPOIDCKerberosLdapAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/EAPOIDCKerberosLdapAdapterTest.java new file mode 100644 index 0000000000..1b0d44b014 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/EAPOIDCKerberosLdapAdapterTest.java @@ -0,0 +1,15 @@ +package org.keycloak.testsuite.adapter; + +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; +import org.junit.Ignore; +import org.keycloak.testsuite.adapter.federation.AbstractKerberosLdapAdapterTest; +import org.keycloak.testsuite.adapter.servlet.AbstractDemoServletsAdapterTest; + +/** + * + * @author tkyjovsk + */ +@AppServerContainer("app-server-eap") +public class EAPOIDCKerberosLdapAdapterTest extends AbstractKerberosLdapAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/EAPOIDCKerberosStandaloneAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/EAPOIDCKerberosStandaloneAdapterTest.java new file mode 100644 index 0000000000..6503467e63 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/EAPOIDCKerberosStandaloneAdapterTest.java @@ -0,0 +1,15 @@ +package org.keycloak.testsuite.adapter; + +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; +import org.junit.Ignore; +import org.keycloak.testsuite.adapter.federation.AbstractKerberosStandaloneAdapterTest; +import org.keycloak.testsuite.adapter.servlet.AbstractDemoServletsAdapterTest; + +/** + * + * @author tkyjovsk + */ +@AppServerContainer("app-server-eap") +public class EAPOIDCKerberosStandaloneAdapterTest extends AbstractKerberosStandaloneAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6OIDCKerberosLdapAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6OIDCKerberosLdapAdapterTest.java new file mode 100644 index 0000000000..88364d6ba9 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6OIDCKerberosLdapAdapterTest.java @@ -0,0 +1,15 @@ +package org.keycloak.testsuite.adapter; + +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; +import org.junit.Ignore; +import org.keycloak.testsuite.adapter.federation.AbstractKerberosLdapAdapterTest; +import org.keycloak.testsuite.adapter.servlet.AbstractDemoServletsAdapterTest; + +/** + * + * @author pdrozd + */ +@AppServerContainer("app-server-eap6") +public class EAP6OIDCKerberosLdapAdapterTest extends AbstractKerberosLdapAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6OIDCKerberosStandaloneAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6OIDCKerberosStandaloneAdapterTest.java new file mode 100644 index 0000000000..266b1c80ec --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6OIDCKerberosStandaloneAdapterTest.java @@ -0,0 +1,15 @@ +package org.keycloak.testsuite.adapter; + +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; +import org.junit.Ignore; +import org.keycloak.testsuite.adapter.federation.AbstractKerberosStandaloneAdapterTest; +import org.keycloak.testsuite.adapter.servlet.AbstractDemoServletsAdapterTest; + +/** + * + * @author pdrozd + */ +@AppServerContainer("app-server-eap6") +public class EAP6OIDCKerberosStandaloneAdapterTest extends AbstractKerberosStandaloneAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/WildflyOIDCKerberosLdapAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/WildflyOIDCKerberosLdapAdapterTest.java new file mode 100644 index 0000000000..64dcb602db --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/WildflyOIDCKerberosLdapAdapterTest.java @@ -0,0 +1,15 @@ +package org.keycloak.testsuite.adapter; + +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; +import org.junit.Ignore; +import org.keycloak.testsuite.adapter.federation.AbstractKerberosLdapAdapterTest; +import org.keycloak.testsuite.adapter.servlet.AbstractDemoServletsAdapterTest; + +/** + * + * @author pdrozd + */ +@AppServerContainer("app-server-wildfly") +public class WildflyOIDCKerberosLdapAdapterTest extends AbstractKerberosLdapAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/WildflyOIDCKerberosStandaloneAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/WildflyOIDCKerberosStandaloneAdapterTest.java new file mode 100644 index 0000000000..ce2c796396 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/WildflyOIDCKerberosStandaloneAdapterTest.java @@ -0,0 +1,15 @@ +package org.keycloak.testsuite.adapter; + +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; +import org.junit.Ignore; +import org.keycloak.testsuite.adapter.federation.AbstractKerberosStandaloneAdapterTest; +import org.keycloak.testsuite.adapter.servlet.AbstractDemoServletsAdapterTest; + +/** + * + * @author pdrozd + */ +@AppServerContainer("app-server-wildfly") +public class WildflyOIDCKerberosStandaloneAdapterTest extends AbstractKerberosStandaloneAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml index dadd13ef39..73c32054b7 100644 --- a/testsuite/integration-arquillian/tests/other/pom.xml +++ b/testsuite/integration-arquillian/tests/other/pom.xml @@ -59,7 +59,7 @@ integration-arquillian-tests-base ${project.version} test-jar - arquillian.xml,keycloak-add-user.json + arquillian.xml,keycloak-add-user.json,kerberos/*