KEYCLOAK-6038 Kerberos cross-realm trust test
This commit is contained in:
parent
060b3b8d0f
commit
575851d45c
25 changed files with 844 additions and 268 deletions
|
@ -40,7 +40,7 @@ public class KerberosUsernamePasswordAuthenticator {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(KerberosUsernamePasswordAuthenticator.class);
|
private static final Logger logger = Logger.getLogger(KerberosUsernamePasswordAuthenticator.class);
|
||||||
|
|
||||||
private final CommonKerberosConfig config;
|
protected final CommonKerberosConfig config;
|
||||||
private LoginContext loginContext;
|
private LoginContext loginContext;
|
||||||
|
|
||||||
public KerberosUsernamePasswordAuthenticator(CommonKerberosConfig config) {
|
public KerberosUsernamePasswordAuthenticator(CommonKerberosConfig config) {
|
||||||
|
|
|
@ -113,6 +113,13 @@ To start a ApacheDS based Kerberos server for testing Kerberos + LDAP sending ru
|
||||||
There are additional system properties you can use to configure (See LDAPEmbeddedServer and KerberosEmbeddedServer class for details) but for testing purposes default values should be good.
|
There are additional system properties you can use to configure (See LDAPEmbeddedServer and KerberosEmbeddedServer class for details) but for testing purposes default values should be good.
|
||||||
By default ApacheDS LDAP server will be running on localhost:10389 and Kerberos KDC on localhost:6088 .
|
By default ApacheDS LDAP server will be running on localhost:10389 and Kerberos KDC on localhost:6088 .
|
||||||
|
|
||||||
|
The alternative is to start Kerberos with the alternative realm KC2.COM instead of default KEYCLOAK.ORG.
|
||||||
|
The ApacheDS server will be then started with all ports shifted by 1000 (EG. LDAP on 11389, Kerberos KDC on 7088).
|
||||||
|
This allows to start 2 kerberos servers in parallel to test things like Kerberos cross-realm trust:
|
||||||
|
|
||||||
|
mvn exec:java -Pkerberos -Dkeycloak.kerberos.realm=KC2.COM
|
||||||
|
|
||||||
|
|
||||||
Once kerberos is running, you can create LDAP Federation provider in Keycloak admin console with same settings like mentioned in previous LDAP section.
|
Once kerberos is running, you can create LDAP Federation provider in Keycloak admin console with same settings like mentioned in previous LDAP section.
|
||||||
But additionally you can enable Kerberos authentication in LDAP provider with the settings like:
|
But additionally you can enable Kerberos authentication in LDAP provider with the settings like:
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.testsuite.rest.resource;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
@ -161,4 +162,20 @@ public class TestLDAPResource {
|
||||||
LDAPObject james = LDAPTestUtils.addLDAPUser(ldapFedProvider, realm, "jameskeycloak", "James", "Brown", "james@email.org", null, "8910");
|
LDAPObject james = LDAPTestUtils.addLDAPUser(ldapFedProvider, realm, "jameskeycloak", "James", "Brown", "james@email.org", null, "8910");
|
||||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, james, "Password1");
|
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, james, "Password1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove specified user directly just from the LDAP server
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
@Path("/remove-ldap-user")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public void removeLDAPUser(@QueryParam("username") String ldapUsername) {
|
||||||
|
ComponentModel ldapCompModel = LDAPTestUtils.getLdapProviderModel(session, realm);
|
||||||
|
UserStorageProviderModel ldapModel = new UserStorageProviderModel(ldapCompModel);
|
||||||
|
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||||
|
|
||||||
|
LDAPTestUtils.removeLDAPUserByUsername(ldapProvider, realm,
|
||||||
|
ldapProvider.getLdapIdentityStore().getConfig(), ldapUsername);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||||
import org.keycloak.storage.ldap.LDAPConfig;
|
import org.keycloak.storage.ldap.LDAPConfig;
|
||||||
|
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||||
import org.keycloak.storage.ldap.LDAPUtils;
|
import org.keycloak.storage.ldap.LDAPUtils;
|
||||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||||
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
|
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
|
||||||
|
@ -120,7 +121,7 @@ public class LDAPTestUtils {
|
||||||
public static ComponentModel getLdapProviderModel(KeycloakSession session, RealmModel realm) {
|
public static ComponentModel getLdapProviderModel(KeycloakSession session, RealmModel realm) {
|
||||||
List<ComponentModel> components = realm.getComponents(realm.getId(), UserStorageProvider.class.getName());
|
List<ComponentModel> components = realm.getComponents(realm.getId(), UserStorageProvider.class.getName());
|
||||||
for (ComponentModel component : components) {
|
for (ComponentModel component : components) {
|
||||||
if ("test-ldap".equals(component.getName())) {
|
if (LDAPStorageProviderFactory.PROVIDER_NAME.equals(component.getProviderId())) {
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.testsuite.client.resources;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
@ -53,4 +54,13 @@ public interface TestingLDAPResource {
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
void prepareGroupsLDAPTest();
|
void prepareGroupsLDAPTest();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove specified user directly just from the LDAP server
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
@Path("/remove-ldap-user")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
void removeLDAPUser(@QueryParam("username") String ldapUsername);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,11 @@ public class KerberosRule extends LDAPRule {
|
||||||
private static final Logger log = Logger.getLogger(KerberosRule.class);
|
private static final Logger log = Logger.getLogger(KerberosRule.class);
|
||||||
|
|
||||||
private final String configLocation;
|
private final String configLocation;
|
||||||
|
private final String kerberosRealm;
|
||||||
|
|
||||||
public KerberosRule(String configLocation) {
|
public KerberosRule(String configLocation, String kerberosRealm) {
|
||||||
this.configLocation = configLocation;
|
this.configLocation = configLocation;
|
||||||
|
this.kerberosRealm = kerberosRealm;
|
||||||
|
|
||||||
// Global kerberos configuration
|
// Global kerberos configuration
|
||||||
String krb5ConfPath = getKrb5ConfPath();
|
String krb5ConfPath = getKrb5ConfPath();
|
||||||
|
@ -61,11 +63,25 @@ public class KerberosRule extends LDAPRule {
|
||||||
return configLocation;
|
return configLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getKerberosRealm() {
|
||||||
|
return kerberosRealm;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LDAPEmbeddedServer createServer() {
|
protected LDAPEmbeddedServer createServer() {
|
||||||
Properties defaultProperties = new Properties();
|
Properties defaultProperties = new Properties();
|
||||||
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_DSF, LDAPEmbeddedServer.DSF_INMEMORY);
|
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_DSF, LDAPEmbeddedServer.DSF_INMEMORY);
|
||||||
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_LDIF_FILE, "classpath:kerberos/users-kerberos.ldif");
|
|
||||||
|
KerberosEmbeddedServer.configureDefaultPropertiesForRealm(this.kerberosRealm, defaultProperties);
|
||||||
|
|
||||||
|
// Improve if more flexibility is needed
|
||||||
|
if ("KEYCLOAK.ORG".equals(kerberosRealm)) {
|
||||||
|
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_LDIF_FILE, "classpath:kerberos/users-kerberos.ldif");
|
||||||
|
} else if ("KC2.COM".equals(kerberosRealm)) {
|
||||||
|
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_LDIF_FILE, "classpath:kerberos/users-kerberos-kc2.ldif");
|
||||||
|
}
|
||||||
|
|
||||||
return new KerberosEmbeddedServer(defaultProperties);
|
return new KerberosEmbeddedServer(defaultProperties);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 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.kerberos;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import org.ietf.jgss.GSSCredential;
|
||||||
|
import org.junit.Assume;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
|
import org.keycloak.common.constants.KerberosConstants;
|
||||||
|
import org.keycloak.common.util.KerberosSerializationUtils;
|
||||||
|
import org.keycloak.events.Details;
|
||||||
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
|
||||||
|
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains just test methods
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public abstract class AbstractKerberosSingleRealmTest extends AbstractKerberosTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spnegoNotAvailableTest() throws Exception {
|
||||||
|
initHttpClient(false);
|
||||||
|
|
||||||
|
String kcLoginPageLocation = oauth.getLoginFormUrl();
|
||||||
|
|
||||||
|
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);
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// KEYCLOAK-7823
|
||||||
|
@Test
|
||||||
|
public void spnegoLoginWithRequiredKerberosAuthExecutionTest() {
|
||||||
|
AuthenticationExecutionModel.Requirement oldRequirement = updateKerberosAuthExecutionRequirement(
|
||||||
|
AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||||
|
Response response = spnegoLogin("hnelson", "secret");
|
||||||
|
updateKerberosAuthExecutionRequirement(oldRequirement);
|
||||||
|
|
||||||
|
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// KEYCLOAK-2102
|
||||||
|
@Test
|
||||||
|
public void spnegoCaseInsensitiveTest() throws Exception {
|
||||||
|
assertSuccessfulSpnegoLogin(getKerberosRule().isCaseSensitiveLogin() ? "MyDuke" : "myduke", "myduke", "theduke");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void usernamePasswordLoginTest() throws Exception {
|
||||||
|
// Change editMode to READ_ONLY
|
||||||
|
updateProviderEditMode(UserStorageProvider.EditMode.READ_ONLY);
|
||||||
|
|
||||||
|
// Login with username/password from kerberos
|
||||||
|
changePasswordPage.open();
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
loginPage.login("jduke", "theduke");
|
||||||
|
changePasswordPage.assertCurrent();
|
||||||
|
|
||||||
|
// Bad existing password
|
||||||
|
changePasswordPage.changePassword("theduke-invalid", "newPass", "newPass");
|
||||||
|
Assert.assertTrue(driver.getPageSource().contains("Invalid existing password."));
|
||||||
|
|
||||||
|
// Change password is not possible as editMode is READ_ONLY
|
||||||
|
changePasswordPage.changePassword("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(UserStorageProvider.EditMode.UNSYNCED);
|
||||||
|
|
||||||
|
// Successfully change password now
|
||||||
|
changePasswordPage.changePassword("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.login("jduke", "theduke");
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
loginPage.login("jduke", "newPass");
|
||||||
|
changePasswordPage.assertCurrent();
|
||||||
|
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());
|
||||||
|
List<UserRepresentation> users = testRealmResource().users().search("jduke", 0, 1);
|
||||||
|
String userId = users.get(0).getId();
|
||||||
|
events.expectLogin()
|
||||||
|
.client("kerberos-app")
|
||||||
|
.user(userId)
|
||||||
|
.detail(Details.USERNAME, "jduke")
|
||||||
|
.assertEvent();
|
||||||
|
|
||||||
|
String codeUrl = spnegoResponse.getLocation().toString();
|
||||||
|
|
||||||
|
assertAuthenticationSuccess(codeUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void credentialDelegationTest() throws Exception {
|
||||||
|
Assume.assumeTrue("Ignoring test as the embedded server is not started", getKerberosRule().isStartEmbeddedLdapServer());
|
||||||
|
// Add kerberos delegation credential mapper
|
||||||
|
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
|
||||||
|
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
|
||||||
|
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
|
||||||
|
true, false);
|
||||||
|
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
|
||||||
|
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
|
||||||
|
Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep);
|
||||||
|
String protocolMapperId = ApiUtil.getCreatedId(response);
|
||||||
|
response.close();
|
||||||
|
|
||||||
|
// SPNEGO login
|
||||||
|
AccessToken token = assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
|
||||||
|
|
||||||
|
// Assert kerberos ticket in the accessToken can be re-used to authenticate against other 3rd party kerberos service (ApacheDS Server in this case)
|
||||||
|
String serializedGssCredential = (String) token.getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL);
|
||||||
|
Assert.assertNotNull(serializedGssCredential);
|
||||||
|
GSSCredential gssCredential = KerberosSerializationUtils.deserializeCredential(serializedGssCredential);
|
||||||
|
String ldapResponse = invokeLdap(gssCredential, token.getPreferredUsername());
|
||||||
|
Assert.assertEquals("Horatio Nelson", ldapResponse);
|
||||||
|
|
||||||
|
// Logout
|
||||||
|
oauth.openLogout();
|
||||||
|
|
||||||
|
// Remove protocolMapper
|
||||||
|
clientResource.getProtocolMappers().delete(protocolMapperId);
|
||||||
|
|
||||||
|
// Login and assert delegated credential not anymore
|
||||||
|
token = assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
|
||||||
|
Assert.assertFalse(token.getOtherClaims().containsKey(KerberosConstants.GSS_DELEGATION_CREDENTIAL));
|
||||||
|
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@
|
||||||
package org.keycloak.testsuite.federation.kerberos;
|
package org.keycloak.testsuite.federation.kerberos;
|
||||||
|
|
||||||
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
||||||
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
@ -34,7 +33,6 @@ import javax.naming.directory.Attributes;
|
||||||
import javax.naming.directory.DirContext;
|
import javax.naming.directory.DirContext;
|
||||||
import javax.naming.directory.InitialDirContext;
|
import javax.naming.directory.InitialDirContext;
|
||||||
import javax.security.sasl.Sasl;
|
import javax.security.sasl.Sasl;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
|
@ -49,34 +47,27 @@ import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||||
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
|
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assume;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.adapters.HttpClientBuilder;
|
import org.keycloak.adapters.HttpClientBuilder;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.authentication.authenticators.browser.SpnegoAuthenticatorFactory;
|
import org.keycloak.authentication.authenticators.browser.SpnegoAuthenticatorFactory;
|
||||||
import org.keycloak.common.constants.KerberosConstants;
|
|
||||||
import org.keycloak.common.util.KerberosSerializationUtils;
|
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.LDAPConstants;
|
import org.keycloak.models.LDAPConstants;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
|
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
|
import org.keycloak.storage.UserStorageProviderModel;
|
||||||
import org.keycloak.testsuite.AbstractAuthTest;
|
import org.keycloak.testsuite.AbstractAuthTest;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
@ -84,9 +75,12 @@ import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.auth.page.AuthRealm;
|
import org.keycloak.testsuite.auth.page.AuthRealm;
|
||||||
import org.keycloak.testsuite.pages.AccountPasswordPage;
|
import org.keycloak.testsuite.pages.AccountPasswordPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
|
import org.keycloak.testsuite.util.KerberosRule;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Contains just helper methods. No test methods.
|
||||||
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractKerberosTest extends AbstractAuthTest {
|
public abstract class AbstractKerberosTest extends AbstractAuthTest {
|
||||||
|
@ -104,13 +98,30 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
|
||||||
@Page
|
@Page
|
||||||
protected AccountPasswordPage changePasswordPage;
|
protected AccountPasswordPage changePasswordPage;
|
||||||
|
|
||||||
|
protected abstract KerberosRule getKerberosRule();
|
||||||
|
|
||||||
protected abstract CommonKerberosConfig getKerberosConfig();
|
protected abstract CommonKerberosConfig getKerberosConfig();
|
||||||
|
|
||||||
protected abstract ComponentRepresentation getUserStorageConfiguration();
|
protected abstract ComponentRepresentation getUserStorageConfiguration();
|
||||||
|
|
||||||
protected abstract void setKrb5ConfPath();
|
|
||||||
|
|
||||||
protected abstract boolean isStartEmbeddedLdapServer();
|
protected ComponentRepresentation getUserStorageConfiguration(String providerName, String providerId) {
|
||||||
|
Map<String,String> kerberosConfig = getKerberosRule().getConfig();
|
||||||
|
MultivaluedHashMap<String, String> config = toComponentConfig(kerberosConfig);
|
||||||
|
|
||||||
|
UserStorageProviderModel model = new UserStorageProviderModel();
|
||||||
|
model.setLastSync(0);
|
||||||
|
model.setChangedSyncPeriod(-1);
|
||||||
|
model.setFullSyncPeriod(-1);
|
||||||
|
model.setName(providerName);
|
||||||
|
model.setPriority(0);
|
||||||
|
model.setProviderId(providerId);
|
||||||
|
model.setConfig(config);
|
||||||
|
|
||||||
|
ComponentRepresentation rep = ModelToRepresentation.toRepresentationWithoutConfig(model);
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||||
|
@ -118,6 +129,11 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
|
||||||
testRealms.add(realmRep);
|
testRealms.add(realmRep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RealmResource testRealmResource() {
|
||||||
|
return adminClient.realm("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@Override
|
@Override
|
||||||
|
@ -127,7 +143,7 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
|
||||||
testRealmPage.setAuthRealm(AuthRealm.TEST);
|
testRealmPage.setAuthRealm(AuthRealm.TEST);
|
||||||
changePasswordPage.realm(AuthRealm.TEST);
|
changePasswordPage.realm(AuthRealm.TEST);
|
||||||
|
|
||||||
setKrb5ConfPath();
|
getKerberosRule().setKrb5ConfPath(testingClient.testing());
|
||||||
|
|
||||||
spnegoSchemeFactory = new KeycloakSPNegoSchemeFactory(getKerberosConfig());
|
spnegoSchemeFactory = new KeycloakSPNegoSchemeFactory(getKerberosConfig());
|
||||||
initHttpClient(true);
|
initHttpClient(true);
|
||||||
|
@ -161,164 +177,31 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
@Test
|
protected AccessToken assertSuccessfulSpnegoLogin(String loginUsername, String expectedUsername, String password) throws Exception {
|
||||||
public void spnegoNotAvailableTest() throws Exception {
|
Response spnegoResponse = spnegoLogin(loginUsername, password);
|
||||||
initHttpClient(false);
|
|
||||||
|
|
||||||
String kcLoginPageLocation = oauth.getLoginFormUrl();
|
|
||||||
|
|
||||||
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);
|
|
||||||
response.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// KEYCLOAK-7823
|
|
||||||
@Test
|
|
||||||
public void spnegoLoginWithRequiredKerberosAuthExecutionTest() {
|
|
||||||
AuthenticationExecutionModel.Requirement oldRequirement = updateKerberosAuthExecutionRequirement(
|
|
||||||
AuthenticationExecutionModel.Requirement.REQUIRED);
|
|
||||||
Response response = spnegoLogin("hnelson", "secret");
|
|
||||||
updateKerberosAuthExecutionRequirement(oldRequirement);
|
|
||||||
|
|
||||||
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected OAuthClient.AccessTokenResponse spnegoLoginTestImpl() throws Exception {
|
|
||||||
Response spnegoResponse = spnegoLogin("hnelson", "secret");
|
|
||||||
Assert.assertEquals(302, spnegoResponse.getStatus());
|
Assert.assertEquals(302, spnegoResponse.getStatus());
|
||||||
|
|
||||||
List<UserRepresentation> users = testRealmResource().users().search("hnelson", 0, 1);
|
List<UserRepresentation> users = testRealmResource().users().search(expectedUsername, 0, 1);
|
||||||
String userId = users.get(0).getId();
|
String userId = users.get(0).getId();
|
||||||
events.expectLogin()
|
events.expectLogin()
|
||||||
.client("kerberos-app")
|
.client("kerberos-app")
|
||||||
.user(userId)
|
.user(userId)
|
||||||
.detail(Details.USERNAME, "hnelson")
|
.detail(Details.USERNAME, expectedUsername)
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
|
|
||||||
String codeUrl = spnegoResponse.getLocation().toString();
|
String codeUrl = spnegoResponse.getLocation().toString();
|
||||||
|
|
||||||
return assertAuthenticationSuccess(codeUrl);
|
OAuthClient.AccessTokenResponse tokenResponse = assertAuthenticationSuccess(codeUrl);
|
||||||
|
|
||||||
|
AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
|
||||||
|
Assert.assertEquals(userId, token.getSubject());
|
||||||
|
Assert.assertEquals(expectedUsername, token.getPreferredUsername());
|
||||||
|
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected abstract boolean isCaseSensitiveLogin();
|
protected String invokeLdap(GSSCredential gssCredential, String username) throws NamingException {
|
||||||
|
|
||||||
// KEYCLOAK-2102
|
|
||||||
@Test
|
|
||||||
public void spnegoCaseInsensitiveTest() throws Exception {
|
|
||||||
Response spnegoResponse = spnegoLogin(isCaseSensitiveLogin() ? "MyDuke" : "myduke", "theduke");
|
|
||||||
Assert.assertEquals(302, spnegoResponse.getStatus());
|
|
||||||
List<UserRepresentation> users = testRealmResource().users().search("myduke", 0, 1);
|
|
||||||
String userId = users.get(0).getId();
|
|
||||||
events.expectLogin()
|
|
||||||
.client("kerberos-app")
|
|
||||||
.user(userId)
|
|
||||||
.detail(Details.USERNAME, "myduke")
|
|
||||||
.assertEvent();
|
|
||||||
|
|
||||||
String codeUrl = spnegoResponse.getLocation().toString();
|
|
||||||
|
|
||||||
assertAuthenticationSuccess(codeUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void usernamePasswordLoginTest() throws Exception {
|
|
||||||
// Change editMode to READ_ONLY
|
|
||||||
updateProviderEditMode(UserStorageProvider.EditMode.READ_ONLY);
|
|
||||||
|
|
||||||
// Login with username/password from kerberos
|
|
||||||
changePasswordPage.open();
|
|
||||||
loginPage.assertCurrent();
|
|
||||||
loginPage.login("jduke", "theduke");
|
|
||||||
changePasswordPage.assertCurrent();
|
|
||||||
|
|
||||||
// Bad existing password
|
|
||||||
changePasswordPage.changePassword("theduke-invalid", "newPass", "newPass");
|
|
||||||
Assert.assertTrue(driver.getPageSource().contains("Invalid existing password."));
|
|
||||||
|
|
||||||
// Change password is not possible as editMode is READ_ONLY
|
|
||||||
changePasswordPage.changePassword("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(UserStorageProvider.EditMode.UNSYNCED);
|
|
||||||
|
|
||||||
// Successfully change password now
|
|
||||||
changePasswordPage.changePassword("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.login("jduke", "theduke");
|
|
||||||
loginPage.assertCurrent();
|
|
||||||
loginPage.login("jduke", "newPass");
|
|
||||||
changePasswordPage.assertCurrent();
|
|
||||||
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());
|
|
||||||
List<UserRepresentation> users = testRealmResource().users().search("jduke", 0, 1);
|
|
||||||
String userId = users.get(0).getId();
|
|
||||||
events.expectLogin()
|
|
||||||
.client("kerberos-app")
|
|
||||||
.user(userId)
|
|
||||||
.detail(Details.USERNAME, "jduke")
|
|
||||||
.assertEvent();
|
|
||||||
|
|
||||||
String codeUrl = spnegoResponse.getLocation().toString();
|
|
||||||
|
|
||||||
assertAuthenticationSuccess(codeUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void credentialDelegationTest() throws Exception {
|
|
||||||
Assume.assumeTrue("Ignoring test as the embedded server is not started", isStartEmbeddedLdapServer());
|
|
||||||
// Add kerberos delegation credential mapper
|
|
||||||
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
|
|
||||||
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
|
|
||||||
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
|
|
||||||
true, false);
|
|
||||||
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
|
|
||||||
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
|
|
||||||
Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep);
|
|
||||||
String protocolMapperId = ApiUtil.getCreatedId(response);
|
|
||||||
response.close();
|
|
||||||
|
|
||||||
// SPNEGO login
|
|
||||||
OAuthClient.AccessTokenResponse tokenResponse = spnegoLoginTestImpl();
|
|
||||||
|
|
||||||
// Assert kerberos ticket in the accessToken can be re-used to authenticate against other 3rd party kerberos service (ApacheDS Server in this case)
|
|
||||||
String accessToken = tokenResponse.getAccessToken();
|
|
||||||
AccessToken token = oauth.verifyToken(accessToken);
|
|
||||||
|
|
||||||
String serializedGssCredential = (String) token.getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL);
|
|
||||||
Assert.assertNotNull(serializedGssCredential);
|
|
||||||
GSSCredential gssCredential = KerberosSerializationUtils.deserializeCredential(serializedGssCredential);
|
|
||||||
String ldapResponse = invokeLdap(gssCredential, token.getPreferredUsername());
|
|
||||||
Assert.assertEquals("Horatio Nelson", ldapResponse);
|
|
||||||
|
|
||||||
// Logout
|
|
||||||
oauth.openLogout();
|
|
||||||
|
|
||||||
// Remove protocolMapper
|
|
||||||
clientResource.getProtocolMappers().delete(protocolMapperId);
|
|
||||||
|
|
||||||
// Login and assert delegated credential not anymore
|
|
||||||
tokenResponse = spnegoLoginTestImpl();
|
|
||||||
accessToken = tokenResponse.getAccessToken();
|
|
||||||
token = oauth.verifyToken(accessToken);
|
|
||||||
Assert.assertFalse(token.getOtherClaims().containsKey(KerberosConstants.GSS_DELEGATION_CREDENTIAL));
|
|
||||||
|
|
||||||
events.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String invokeLdap(GSSCredential gssCredential, String username) throws NamingException {
|
|
||||||
Hashtable env = new Hashtable(11);
|
Hashtable env = new Hashtable(11);
|
||||||
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||||
env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
|
env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
|
||||||
|
@ -462,7 +345,8 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
|
||||||
testRealmResource().components().component(kerberosProvider.getId()).update(kerberosProvider);
|
testRealmResource().components().component(kerberosProvider.getId()).update(kerberosProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthenticationExecutionModel.Requirement updateKerberosAuthExecutionRequirement(AuthenticationExecutionModel.Requirement requirement) {
|
|
||||||
|
protected AuthenticationExecutionModel.Requirement updateKerberosAuthExecutionRequirement(AuthenticationExecutionModel.Requirement requirement) {
|
||||||
Optional<AuthenticationExecutionInfoRepresentation> kerberosAuthExecutionOpt = testRealmResource()
|
Optional<AuthenticationExecutionInfoRepresentation> kerberosAuthExecutionOpt = testRealmResource()
|
||||||
.flows()
|
.flows()
|
||||||
.getExecutions(DefaultAuthenticationFlows.BROWSER_FLOW)
|
.getExecutions(DefaultAuthenticationFlows.BROWSER_FLOW)
|
||||||
|
@ -484,14 +368,8 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
|
||||||
return oldRequirement;
|
return oldRequirement;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public RealmResource testRealmResource() {
|
|
||||||
return adminClient.realm("test");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private static MultivaluedHashMap<String, String> toComponentConfig(Map<String, String> ldapConfig) {
|
||||||
// TODO: Use LDAPTestUtils.toComponentConfig once it's migrated to new testsuite
|
|
||||||
public static MultivaluedHashMap<String, String> toComponentConfig(Map<String, String> ldapConfig) {
|
|
||||||
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
|
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
|
||||||
for (Map.Entry<String, String> entry : ldapConfig.entrySet()) {
|
for (Map.Entry<String, String> entry : ldapConfig.entrySet()) {
|
||||||
config.add(entry.getKey(), entry.getValue());
|
config.add(entry.getKey(), entry.getValue());
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 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.kerberos;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import org.jboss.arquillian.container.test.api.Deployment;
|
||||||
|
import org.jboss.arquillian.container.test.api.TargetsContainer;
|
||||||
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
|
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
|
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||||
|
import org.keycloak.storage.ldap.kerberos.LDAPProviderKerberosConfig;
|
||||||
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.federation.ldap.LDAPTestContext;
|
||||||
|
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
|
||||||
|
import org.keycloak.testsuite.util.KerberosRule;
|
||||||
|
import org.keycloak.testsuite.util.LDAPTestUtils;
|
||||||
|
import org.keycloak.util.ldap.KerberosEmbeddedServer;
|
||||||
|
import org.keycloak.util.ldap.LDAPEmbeddedServer;
|
||||||
|
|
||||||
|
import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
public class KerberosLdapCrossRealmTrustTest extends AbstractKerberosTest {
|
||||||
|
|
||||||
|
@Deployment
|
||||||
|
@TargetsContainer(AUTH_SERVER_CURRENT)
|
||||||
|
public static WebArchive deploy() {
|
||||||
|
return RunOnServerDeployment.create(UserResource.class, LDAPEmbeddedServer.class, KerberosEmbeddedServer.class)
|
||||||
|
.addPackages(true,
|
||||||
|
"org.keycloak.testsuite",
|
||||||
|
"org.keycloak.testsuite.federation.ldap",
|
||||||
|
"org.keycloak.testsuite.federation.kerberos");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-ldap-crt-connection.properties";
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION, KerberosEmbeddedServer.DEFAULT_KERBEROS_REALM);
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static KerberosRule kerberosRule2 = new KerberosRule(PROVIDER_CONFIG_LOCATION, KerberosEmbeddedServer.DEFAULT_KERBEROS_REALM_2);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KerberosRule getKerberosRule() {
|
||||||
|
return kerberosRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CommonKerberosConfig getKerberosConfig() {
|
||||||
|
return new LDAPProviderKerberosConfig(getUserStorageConfiguration());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ComponentRepresentation getUserStorageConfiguration() {
|
||||||
|
return getUserStorageConfiguration("kerberos-ldap", LDAPStorageProviderFactory.PROVIDER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test01SpnegoLoginCRTSuccess() throws Exception {
|
||||||
|
// Login as user from realm KC2.COM . Realm KEYCLOAK.ORG will trust us
|
||||||
|
AccessToken token = assertSuccessfulSpnegoLogin("hnelson2@KC2.COM", "hnelson2", "secret");
|
||||||
|
|
||||||
|
Assert.assertEquals(token.getEmail(), "hnelson2@kc2.com");
|
||||||
|
assertUser("hnelson2", "hnelson2@kc2.com", "Horatio", "Nelson", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test02DisableTrust() throws Exception {
|
||||||
|
// Remove the LDAP entry corresponding to the Kerberos principal krbtgt/KEYCLOAK.ORG@KC2.COM
|
||||||
|
// This will effectively disable kerberos cross-realm trust
|
||||||
|
testingClient.testing().ldap("test").removeLDAPUser("krbtgt2");
|
||||||
|
|
||||||
|
|
||||||
|
// There is no trust among kerberos realms anymore. SPNEGO shouldn't work. There would be failure even on Apache HTTP client side
|
||||||
|
// as it's not possible to start GSS context ( initSecContext ) due the missing trust among realms.
|
||||||
|
try {
|
||||||
|
Response spnegoResponse = spnegoLogin("hnelson2@KC2.COM", "secret");
|
||||||
|
Assert.fail("Not expected to successfully login");
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -17,40 +17,41 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.federation.kerberos;
|
package org.keycloak.testsuite.federation.kerberos;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import org.apache.directory.server.core.api.authn.ppolicy.PasswordPolicyConfiguration;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||||
import org.keycloak.models.PasswordPolicy;
|
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
|
||||||
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||||
import org.keycloak.storage.ldap.kerberos.LDAPProviderKerberosConfig;
|
import org.keycloak.storage.ldap.kerberos.LDAPProviderKerberosConfig;
|
||||||
import org.keycloak.testsuite.util.KerberosRule;
|
import org.keycloak.testsuite.util.KerberosRule;
|
||||||
|
import org.keycloak.util.ldap.KerberosEmbeddedServer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Test for the LDAPStorageProvider with kerberos enabled (kerberos with LDAP integration)
|
||||||
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class KerberosLdapTest extends AbstractKerberosTest {
|
public class KerberosLdapTest extends AbstractKerberosSingleRealmTest {
|
||||||
|
|
||||||
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-ldap-connection.properties";
|
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-ldap-connection.properties";
|
||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
public static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION);
|
public static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION, KerberosEmbeddedServer.DEFAULT_KERBEROS_REALM);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KerberosRule getKerberosRule() {
|
||||||
|
return kerberosRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CommonKerberosConfig getKerberosConfig() {
|
protected CommonKerberosConfig getKerberosConfig() {
|
||||||
|
@ -59,43 +60,14 @@ public class KerberosLdapTest extends AbstractKerberosTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ComponentRepresentation getUserStorageConfiguration() {
|
protected ComponentRepresentation getUserStorageConfiguration() {
|
||||||
Map<String,String> kerberosConfig = kerberosRule.getConfig();
|
return getUserStorageConfiguration("kerberos-ldap", LDAPStorageProviderFactory.PROVIDER_NAME);
|
||||||
MultivaluedHashMap<String, String> config = toComponentConfig(kerberosConfig);
|
|
||||||
|
|
||||||
UserStorageProviderModel model = new UserStorageProviderModel();
|
|
||||||
model.setLastSync(0);
|
|
||||||
model.setChangedSyncPeriod(-1);
|
|
||||||
model.setFullSyncPeriod(-1);
|
|
||||||
model.setName("kerberos-ldap");
|
|
||||||
model.setPriority(0);
|
|
||||||
model.setProviderId(LDAPStorageProviderFactory.PROVIDER_NAME);
|
|
||||||
model.setConfig(config);
|
|
||||||
|
|
||||||
ComponentRepresentation rep = ModelToRepresentation.toRepresentationWithoutConfig(model);
|
|
||||||
return rep;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isCaseSensitiveLogin() {
|
|
||||||
return kerberosRule.isCaseSensitiveLogin();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isStartEmbeddedLdapServer() {
|
|
||||||
return kerberosRule.isStartEmbeddedLdapServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setKrb5ConfPath() {
|
|
||||||
kerberosRule.setKrb5ConfPath(testingClient.testing());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void spnegoLoginTest() throws Exception {
|
public void spnegoLoginTest() throws Exception {
|
||||||
spnegoLoginTestImpl();
|
assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
|
||||||
|
|
||||||
// Assert user was imported and hasn't any required action on him. Profile info is synced from LDAP
|
// 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);
|
assertUser("hnelson", "hnelson@keycloak.org", "Horatio", "Nelson", false);
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 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.kerberos;
|
||||||
|
|
||||||
|
import org.jboss.arquillian.container.test.api.Deployment;
|
||||||
|
import org.jboss.arquillian.container.test.api.TargetsContainer;
|
||||||
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
|
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||||
|
import org.keycloak.federation.kerberos.KerberosConfig;
|
||||||
|
import org.keycloak.federation.kerberos.KerberosFederationProviderFactory;
|
||||||
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
|
import org.keycloak.testsuite.federation.ldap.AbstractLDAPTest;
|
||||||
|
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
|
||||||
|
import org.keycloak.testsuite.util.KerberosRule;
|
||||||
|
import org.keycloak.util.ldap.KerberosEmbeddedServer;
|
||||||
|
|
||||||
|
import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
public class KerberosStandaloneCrossRealmTrustTest extends AbstractKerberosTest {
|
||||||
|
|
||||||
|
@Deployment
|
||||||
|
@TargetsContainer(AUTH_SERVER_CURRENT)
|
||||||
|
public static WebArchive deploy() {
|
||||||
|
return RunOnServerDeployment.create(UserResource.class, AbstractLDAPTest.class, AbstractKerberosTest.class)
|
||||||
|
.addPackages(true,
|
||||||
|
"org.keycloak.testsuite",
|
||||||
|
"org.keycloak.testsuite.federation.ldap",
|
||||||
|
"org.keycloak.testsuite.federation.kerberos");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-standalone-connection.properties";
|
||||||
|
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION, KerberosEmbeddedServer.DEFAULT_KERBEROS_REALM);
|
||||||
|
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static KerberosRule kerberosRule2 = new KerberosRule(PROVIDER_CONFIG_LOCATION, KerberosEmbeddedServer.DEFAULT_KERBEROS_REALM_2);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KerberosRule getKerberosRule() {
|
||||||
|
return kerberosRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CommonKerberosConfig getKerberosConfig() {
|
||||||
|
return new KerberosConfig(getUserStorageConfiguration());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ComponentRepresentation getUserStorageConfiguration() {
|
||||||
|
return getUserStorageConfiguration("kerberos-standalone", KerberosFederationProviderFactory.PROVIDER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test01spnegoLoginSameRealmTest() throws Exception {
|
||||||
|
assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
|
||||||
|
assertUser("hnelson", "hnelson@keycloak.org", null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test02spnegoLoginDifferentRealmTest() throws Exception {
|
||||||
|
// Cross-realm trust login. Realm KEYCLOAK.ORG trusts realm KC2.COM.
|
||||||
|
// TODO: email hnelson2@keycloak.org is not very good. Will be better to have more flexibility for mapping of kerberos principals to Keycloak UserModel in KerberosFederationProvider (if needed)
|
||||||
|
assertSuccessfulSpnegoLogin("hnelson2@KC2.COM", "hnelson2", "secret");
|
||||||
|
assertUser("hnelson2", "hnelson2@keycloak.org", null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -18,11 +18,7 @@
|
||||||
package org.keycloak.testsuite.federation.kerberos;
|
package org.keycloak.testsuite.federation.kerberos;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
|
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
|
@ -33,28 +29,34 @@ import org.junit.Assert;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.common.constants.KerberosConstants;
|
import org.keycloak.common.constants.KerberosConstants;
|
||||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
|
||||||
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||||
import org.keycloak.federation.kerberos.KerberosConfig;
|
import org.keycloak.federation.kerberos.KerberosConfig;
|
||||||
import org.keycloak.federation.kerberos.KerberosFederationProviderFactory;
|
import org.keycloak.federation.kerberos.KerberosFederationProviderFactory;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
|
||||||
import org.keycloak.testsuite.ActionURIUtils;
|
import org.keycloak.testsuite.ActionURIUtils;
|
||||||
import org.keycloak.testsuite.util.KerberosRule;
|
import org.keycloak.testsuite.util.KerberosRule;
|
||||||
|
import org.keycloak.util.ldap.KerberosEmbeddedServer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Test for the KerberosFederationProvider (kerberos without LDAP integration)
|
||||||
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class KerberosStandaloneTest extends AbstractKerberosTest {
|
public class KerberosStandaloneTest extends AbstractKerberosSingleRealmTest {
|
||||||
|
|
||||||
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-standalone-connection.properties";
|
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-standalone-connection.properties";
|
||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
public static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION);
|
public static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION, KerberosEmbeddedServer.DEFAULT_KERBEROS_REALM);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KerberosRule getKerberosRule() {
|
||||||
|
return kerberosRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CommonKerberosConfig getKerberosConfig() {
|
protected CommonKerberosConfig getKerberosConfig() {
|
||||||
|
@ -63,44 +65,15 @@ public class KerberosStandaloneTest extends AbstractKerberosTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ComponentRepresentation getUserStorageConfiguration() {
|
protected ComponentRepresentation getUserStorageConfiguration() {
|
||||||
Map<String,String> kerberosConfig = kerberosRule.getConfig();
|
return getUserStorageConfiguration("kerberos-standalone", KerberosFederationProviderFactory.PROVIDER_NAME);
|
||||||
MultivaluedHashMap<String, String> config = toComponentConfig(kerberosConfig);
|
|
||||||
|
|
||||||
UserStorageProviderModel model = new UserStorageProviderModel();
|
|
||||||
model.setLastSync(0);
|
|
||||||
model.setChangedSyncPeriod(-1);
|
|
||||||
model.setFullSyncPeriod(-1);
|
|
||||||
model.setName("kerberos-standalone");
|
|
||||||
model.setPriority(0);
|
|
||||||
model.setProviderId(KerberosFederationProviderFactory.PROVIDER_NAME);
|
|
||||||
model.setConfig(config);
|
|
||||||
|
|
||||||
ComponentRepresentation rep = ModelToRepresentation.toRepresentationWithoutConfig(model);
|
|
||||||
return rep;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isCaseSensitiveLogin() {
|
|
||||||
return kerberosRule.isCaseSensitiveLogin();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isStartEmbeddedLdapServer() {
|
|
||||||
return kerberosRule.isStartEmbeddedLdapServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setKrb5ConfPath() {
|
|
||||||
kerberosRule.setKrb5ConfPath(testingClient.testing());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void spnegoLoginTest() throws Exception {
|
public void spnegoLoginTest() throws Exception {
|
||||||
spnegoLoginTestImpl();
|
assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
|
||||||
|
|
||||||
// Assert user was imported and hasn't any required action on him. Profile info is synced from LDAP
|
// Assert user was imported and hasn't any required action on him. Profile info is NOT synced from LDAP. Just username is filled and email is "guessed"
|
||||||
assertUser("hnelson", "hnelson@" + kerberosRule.getConfig().get(KerberosConstants.KERBEROS_REALM).toLowerCase(), null, null, false);
|
assertUser("hnelson", "hnelson@" + kerberosRule.getConfig().get(KerberosConstants.KERBEROS_REALM).toLowerCase(), null, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||||
import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
|
import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
|
||||||
|
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
|
import javax.security.auth.login.LoginException;
|
||||||
|
|
||||||
import java.security.PrivilegedExceptionAction;
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,7 +76,19 @@ public class KeycloakSPNegoSchemeFactory extends SPNegoSchemeFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected byte[] generateGSSToken(byte[] input, Oid oid, String authServer, Credentials credentials) throws GSSException {
|
protected byte[] generateGSSToken(byte[] input, Oid oid, String authServer, Credentials credentials) throws GSSException {
|
||||||
KerberosUsernamePasswordAuthenticator authenticator = new KerberosUsernamePasswordAuthenticator(kerberosConfig);
|
KerberosUsernamePasswordAuthenticator authenticator = new KerberosUsernamePasswordAuthenticator(kerberosConfig) {
|
||||||
|
|
||||||
|
// Disable strict check for the configured kerberos realm, which is on super-method
|
||||||
|
@Override
|
||||||
|
protected String getKerberosPrincipal(String username) throws LoginException {
|
||||||
|
if (username.contains("@")) {
|
||||||
|
return username;
|
||||||
|
} else {
|
||||||
|
return username + "@" + config.getKerberosRealm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Subject clientSubject = authenticator.authenticateSubject(username, password);
|
Subject clientSubject = authenticator.authenticateSubject(username, password);
|
||||||
|
|
||||||
|
@ -109,7 +123,8 @@ public class KeycloakSPNegoSchemeFactory extends SPNegoSchemeFactory {
|
||||||
token = new byte[0];
|
token = new byte[0];
|
||||||
}
|
}
|
||||||
GSSManager manager = getManager();
|
GSSManager manager = getManager();
|
||||||
GSSName serverName = manager.createName("HTTP/" + authServer + "@" + kerberosConfig.getKerberosRealm(), null);
|
String httPrincipal = kerberosConfig.getServerPrincipal().replaceFirst("/.*@", "/" + authServer + "@");
|
||||||
|
GSSName serverName = manager.createName(httPrincipal, null);
|
||||||
GSSContext gssContext = manager.createContext(
|
GSSContext gssContext = manager.createContext(
|
||||||
serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME);
|
serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME);
|
||||||
gssContext.requestMutualAuth(true);
|
gssContext.requestMutualAuth(true);
|
||||||
|
|
|
@ -24,20 +24,12 @@ import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.FixMethodOrder;
|
import org.junit.FixMethodOrder;
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runners.MethodSorters;
|
import org.junit.runners.MethodSorters;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
|
||||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||||
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
|
||||||
import org.keycloak.storage.ldap.LDAPUtils;
|
|
||||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
|
||||||
import org.keycloak.storage.ldap.mappers.membership.LDAPGroupMapperMode;
|
import org.keycloak.storage.ldap.mappers.membership.LDAPGroupMapperMode;
|
||||||
import org.keycloak.storage.ldap.mappers.membership.MembershipType;
|
|
||||||
import org.keycloak.storage.ldap.mappers.membership.group.GroupLDAPStorageMapperFactory;
|
import org.keycloak.storage.ldap.mappers.membership.group.GroupLDAPStorageMapperFactory;
|
||||||
import org.keycloak.storage.ldap.mappers.membership.group.GroupMapperConfig;
|
import org.keycloak.storage.ldap.mappers.membership.group.GroupMapperConfig;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#
|
||||||
|
# Copyright 2017 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Using LDAP from "kc2" domain, but HTTP principal is configured to use KEYCLOAK.ORG domain.
|
||||||
|
# Realm KC2.COM has the purpose just for the client side login (KerberosUsernamePasswordAuthenticator used by Apache HTTP client in the test)
|
||||||
|
idm.test.ldap.connection.url=ldap\://localhost\:11389
|
||||||
|
idm.test.ldap.base.dn=dc\=kc2,dc\=com
|
||||||
|
idm.test.ldap.roles.dn.suffix=ou\=Roles,dc\=kc2,dc\=com
|
||||||
|
idm.test.ldap.group.dn.suffix=ou\=Groups,dc\=kc2,dc\=com
|
||||||
|
idm.test.ldap.user.dn.suffix=ou\=People,dc\=kc2,dc\=com
|
||||||
|
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=KC2.COM
|
||||||
|
idm.test.kerberos.server.principal=HTTP/localhost@KEYCLOAK.ORG
|
||||||
|
idm.test.kerberos.debug=true
|
||||||
|
idm.test.kerberos.use.kerberos.for.password.authentication=true
|
|
@ -14,6 +14,9 @@
|
||||||
KEYCLOAK.ORG = {
|
KEYCLOAK.ORG = {
|
||||||
kdc = localhost:6088
|
kdc = localhost:6088
|
||||||
}
|
}
|
||||||
|
KC2.COM = {
|
||||||
|
kdc = localhost:7088
|
||||||
|
}
|
||||||
|
|
||||||
[domain_realm]
|
[domain_realm]
|
||||||
localhost = KEYCLOAK.ORG
|
localhost = KEYCLOAK.ORG
|
|
@ -0,0 +1,104 @@
|
||||||
|
dn: dc=kc2,dc=com
|
||||||
|
objectclass: dcObject
|
||||||
|
objectclass: organization
|
||||||
|
o: Kc2
|
||||||
|
dc: Kc2
|
||||||
|
|
||||||
|
dn: ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: organizationalUnit
|
||||||
|
objectClass: top
|
||||||
|
ou: People
|
||||||
|
|
||||||
|
dn: uid=krbtgt,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: KDC Service
|
||||||
|
sn: Service
|
||||||
|
uid: krbtgt
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: krbtgt/KC2.COM@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
# Cross-realm trust support! Realm KEYCLOAK.ORG will trust the realm KC2.COM
|
||||||
|
dn: uid=krbtgt2,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: KDC Service
|
||||||
|
sn: Service
|
||||||
|
uid: krbtgt2
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=ldap,ou=People,dc=kc2,dc=com
|
||||||
|
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=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: HTTP
|
||||||
|
sn: Service
|
||||||
|
uid: HTTP
|
||||||
|
userPassword: httppwd
|
||||||
|
krb5PrincipalName: HTTP/${hostname}@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=hnelson2,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: Horatio
|
||||||
|
sn: Nelson
|
||||||
|
mail: hnelson2@kc2.com
|
||||||
|
uid: hnelson2
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: hnelson2@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=jduke2,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: Java
|
||||||
|
sn: Duke
|
||||||
|
mail: jduke2@keycloak.org
|
||||||
|
uid: jduke2
|
||||||
|
userPassword: theduke
|
||||||
|
krb5PrincipalName: jduke2@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=gsstestserver,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: gsstestserver
|
||||||
|
sn: Service
|
||||||
|
uid: gsstestserver
|
||||||
|
userPassword: gsstestpwd
|
||||||
|
krb5PrincipalName: gsstestserver/xxx@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
|
@ -22,6 +22,20 @@ userPassword: secret
|
||||||
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KEYCLOAK.ORG
|
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KEYCLOAK.ORG
|
||||||
krb5KeyVersionNumber: 0
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
# Cross-realm trust support! Realm KEYCLOAK.ORG will trust the realm KC2.COM
|
||||||
|
dn: uid=krbtgt2,ou=People,dc=keycloak,dc=org
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: KDC Service
|
||||||
|
sn: Service
|
||||||
|
uid: krbtgt2
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
dn: uid=ldap,ou=People,dc=keycloak,dc=org
|
dn: uid=ldap,ou=People,dc=keycloak,dc=org
|
||||||
objectClass: top
|
objectClass: top
|
||||||
objectClass: person
|
objectClass: person
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
<module name="org.keycloak.keycloak-services"/>
|
<module name="org.keycloak.keycloak-services"/>
|
||||||
<module name="org.keycloak.keycloak-model-infinispan"/>
|
<module name="org.keycloak.keycloak-model-infinispan"/>
|
||||||
<module name="org.keycloak.keycloak-model-jpa"/>
|
<module name="org.keycloak.keycloak-model-jpa"/>
|
||||||
|
<module name="org.keycloak.keycloak-kerberos-federation"/>
|
||||||
<module name="org.keycloak.keycloak-ldap-federation"/>
|
<module name="org.keycloak.keycloak-ldap-federation"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</deployment>
|
</deployment>
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>log4j</groupId>
|
<groupId>log4j</groupId>
|
||||||
<artifactId>log4j</artifactId>
|
<artifactId>log4j</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
|
|
@ -106,7 +106,8 @@ class InMemoryDirectoryServiceFactory implements DirectoryServiceFactory {
|
||||||
|
|
||||||
// EhCache in disabled-like-mode
|
// EhCache in disabled-like-mode
|
||||||
Configuration ehCacheConfig = new Configuration();
|
Configuration ehCacheConfig = new Configuration();
|
||||||
CacheConfiguration defaultCache = new CacheConfiguration("default", 1).eternal(false).timeToIdleSeconds(30)
|
ehCacheConfig.setName(name);
|
||||||
|
CacheConfiguration defaultCache = new CacheConfiguration(name + "-default", 1).eternal(false).timeToIdleSeconds(30)
|
||||||
.timeToLiveSeconds(30).overflowToDisk(false);
|
.timeToLiveSeconds(30).overflowToDisk(false);
|
||||||
ehCacheConfig.addDefaultCache(defaultCache);
|
ehCacheConfig.addDefaultCache(defaultCache);
|
||||||
CacheService cacheService = new CacheService(new CacheManager(ehCacheConfig));
|
CacheService cacheService = new CacheService(new CacheManager(ehCacheConfig));
|
||||||
|
|
|
@ -60,7 +60,9 @@ public class KerberosEmbeddedServer extends LDAPEmbeddedServer {
|
||||||
|
|
||||||
private static final String DEFAULT_KERBEROS_LDIF_FILE = "classpath:kerberos/default-users.ldif";
|
private static final String DEFAULT_KERBEROS_LDIF_FILE = "classpath:kerberos/default-users.ldif";
|
||||||
|
|
||||||
private static final String DEFAULT_KERBEROS_REALM = "KEYCLOAK.ORG";
|
public static final String DEFAULT_KERBEROS_REALM = "KEYCLOAK.ORG";
|
||||||
|
public static final String DEFAULT_KERBEROS_REALM_2 = "KC2.COM";
|
||||||
|
|
||||||
private static final String DEFAULT_KDC_PORT = "6088";
|
private static final String DEFAULT_KDC_PORT = "6088";
|
||||||
private static final String DEFAULT_KDC_ENCRYPTION_TYPES = "aes128-cts-hmac-sha1-96, des-cbc-md5, des3-cbc-sha1-kd";
|
private static final String DEFAULT_KDC_ENCRYPTION_TYPES = "aes128-cts-hmac-sha1-96, des-cbc-md5, des3-cbc-sha1-kd";
|
||||||
|
|
||||||
|
@ -75,9 +77,31 @@ public class KerberosEmbeddedServer extends LDAPEmbeddedServer {
|
||||||
Properties defaultProperties = new Properties();
|
Properties defaultProperties = new Properties();
|
||||||
defaultProperties.put(PROPERTY_DSF, DSF_FILE);
|
defaultProperties.put(PROPERTY_DSF, DSF_FILE);
|
||||||
|
|
||||||
|
String kerberosRealm = System.getProperty("keycloak.kerberos.realm", DEFAULT_KERBEROS_REALM);
|
||||||
|
configureDefaultPropertiesForRealm(kerberosRealm, defaultProperties);
|
||||||
|
|
||||||
execute(args, defaultProperties);
|
execute(args, defaultProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void configureDefaultPropertiesForRealm(String kerberosRealm, Properties properties) {
|
||||||
|
log.infof("Using kerberos realm: %s", kerberosRealm);
|
||||||
|
if (DEFAULT_KERBEROS_REALM.equals(kerberosRealm)) {
|
||||||
|
// No more configs
|
||||||
|
} else if (DEFAULT_KERBEROS_REALM_2.equals(kerberosRealm)) {
|
||||||
|
properties.put(PROPERTY_BASE_DN, "dc=kc2,dc=com");
|
||||||
|
properties.put(PROPERTY_BIND_PORT, "11389");
|
||||||
|
properties.put(PROPERTY_BIND_LDAPS_PORT, "11636");
|
||||||
|
properties.put(PROPERTY_LDIF_FILE, "classpath:kerberos/default-users-kc2.ldif");
|
||||||
|
properties.put(PROPERTY_KERBEROS_REALM, DEFAULT_KERBEROS_REALM_2);
|
||||||
|
properties.put(PROPERTY_KDC_PORT, "7088");
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Valid values for kerberos realm are [ " + DEFAULT_KERBEROS_REALM + " , "
|
||||||
|
+ DEFAULT_KERBEROS_REALM_2 + " ]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void execute(String[] args, Properties defaultProperties) throws Exception {
|
public static void execute(String[] args, Properties defaultProperties) throws Exception {
|
||||||
final KerberosEmbeddedServer kerberosEmbeddedServer = new KerberosEmbeddedServer(defaultProperties);
|
final KerberosEmbeddedServer kerberosEmbeddedServer = new KerberosEmbeddedServer(defaultProperties);
|
||||||
kerberosEmbeddedServer.init();
|
kerberosEmbeddedServer.init();
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
dn: dc=kc2,dc=com
|
||||||
|
objectclass: dcObject
|
||||||
|
objectclass: organization
|
||||||
|
o: Kc2
|
||||||
|
dc: Kc2
|
||||||
|
|
||||||
|
dn: ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: organizationalUnit
|
||||||
|
objectClass: top
|
||||||
|
ou: People
|
||||||
|
|
||||||
|
dn: uid=krbtgt,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: KDC Service
|
||||||
|
sn: Service
|
||||||
|
uid: krbtgt
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: krbtgt/KC2.COM@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
# Cross-realm trust support! Realm KEYCLOAK.ORG will trust the realm KC2.COM
|
||||||
|
dn: uid=krbtgt2,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: KDC Service
|
||||||
|
sn: Service
|
||||||
|
uid: krbtgt2
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=ldap,ou=People,dc=kc2,dc=com
|
||||||
|
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=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: HTTP
|
||||||
|
sn: Service
|
||||||
|
uid: HTTP
|
||||||
|
userPassword: httppwd
|
||||||
|
krb5PrincipalName: HTTP/${hostname}@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=hnelson2,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: Horatio
|
||||||
|
sn: Nelson
|
||||||
|
mail: hnelson2@kc2.com
|
||||||
|
uid: hnelson2
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: hnelson2@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=jduke2,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: Java
|
||||||
|
sn: Duke
|
||||||
|
mail: jduke2@keycloak.org
|
||||||
|
uid: jduke2
|
||||||
|
userPassword: theduke
|
||||||
|
krb5PrincipalName: jduke2@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
dn: uid=gsstestserver,ou=People,dc=kc2,dc=com
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: gsstestserver
|
||||||
|
sn: Service
|
||||||
|
uid: gsstestserver
|
||||||
|
userPassword: gsstestpwd
|
||||||
|
krb5PrincipalName: gsstestserver/xxx@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
|
@ -22,6 +22,20 @@ userPassword: secret
|
||||||
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KEYCLOAK.ORG
|
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KEYCLOAK.ORG
|
||||||
krb5KeyVersionNumber: 0
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
|
# Cross-realm trust support! Realm KEYCLOAK.ORG will trust the realm KC2.COM
|
||||||
|
dn: uid=krbtgt2,ou=People,dc=keycloak,dc=org
|
||||||
|
objectClass: top
|
||||||
|
objectClass: person
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
objectClass: krb5principal
|
||||||
|
objectClass: krb5kdcentry
|
||||||
|
cn: KDC Service
|
||||||
|
sn: Service
|
||||||
|
uid: krbtgt2
|
||||||
|
userPassword: secret
|
||||||
|
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KC2.COM
|
||||||
|
krb5KeyVersionNumber: 0
|
||||||
|
|
||||||
dn: uid=ldap,ou=People,dc=keycloak,dc=org
|
dn: uid=ldap,ou=People,dc=keycloak,dc=org
|
||||||
objectClass: top
|
objectClass: top
|
||||||
objectClass: person
|
objectClass: person
|
||||||
|
|
|
@ -24,3 +24,6 @@ log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
|
||||||
log4j.logger.org.keycloak=info
|
log4j.logger.org.keycloak=info
|
||||||
log4j.logger.org.apache.directory.api=warn
|
log4j.logger.org.apache.directory.api=warn
|
||||||
log4j.logger.org.apache.directory.server.core=warn
|
log4j.logger.org.apache.directory.server.core=warn
|
||||||
|
|
||||||
|
# Enable to view detailed AS REQ and TGS REQ requests to embedded Kerberos server
|
||||||
|
#log4j.logger.org.apache.directory.server.kerberos=debug
|
Loading…
Reference in a new issue