LDAP polishing

This commit is contained in:
mposolda 2016-12-19 17:29:04 +01:00
parent ac00f7fee2
commit 4b6df5d489
8 changed files with 89 additions and 23 deletions

View file

@ -129,7 +129,7 @@
"config" : { "config" : {
"Claim JSON Type" : "String", "Claim JSON Type" : "String",
"user.attribute" : "picture", "user.attribute" : "picture",
"claim.name" : "profile_picture", "claim.name" : "picture",
"multivalued": "false", "multivalued": "false",
"id.token.claim" : "true", "id.token.claim" : "true",
"access.token.claim" : "true" "access.token.claim" : "true"

View file

@ -45,23 +45,14 @@ public class LDAPPictureServlet extends HttpServlet {
KeycloakSecurityContext securityContext = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName()); KeycloakSecurityContext securityContext = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
IDToken idToken = securityContext.getIdToken(); IDToken idToken = securityContext.getIdToken();
// TODO: Use idToken.getPicture() instead String profilePicture = idToken.getPicture();
Object profilePicture = idToken.getOtherClaims().get("profile_picture");
if (profilePicture != null) { if (profilePicture != null) {
String base64EncodedPicture = getBase64EncodedPicture(profilePicture); byte[] decodedPicture = Base64.decode(profilePicture);
byte[] decodedPicture = Base64.decode(base64EncodedPicture);
outputStream.write(decodedPicture); outputStream.write(decodedPicture);
} }
outputStream.flush(); outputStream.flush();
} }
private String getBase64EncodedPicture(Object profilePicture) {
if (profilePicture instanceof List) {
return ((List) profilePicture).get(0).toString();
} else {
return profilePicture.toString();
}
}
} }

View file

@ -37,21 +37,18 @@
<p><b>Full Name: </b><%=idToken.getName()%></p> <p><b>Full Name: </b><%=idToken.getName()%></p>
<p><b>First: </b><%=idToken.getGivenName()%></p> <p><b>First: </b><%=idToken.getGivenName()%></p>
<p><b>Last: </b><%=idToken.getFamilyName()%></p> <p><b>Last: </b><%=idToken.getFamilyName()%></p>
<% if (idToken.getPicture() != null) { %>
<p><b>Profile picture: </b><img src='/ldap-portal/picture' /></p>
<% } %>
<hr /> <hr />
<h2>ID Token - other claims</h2> <h2>ID Token - other claims</h2>
<% <%
for (Map.Entry<String, Object> claim : idToken.getOtherClaims().entrySet()) { for (Map.Entry<String, Object> claim : idToken.getOtherClaims().entrySet()) {
if (!claim.getKey().equals("profile_picture")) {
%> %>
<p><b><%= claim.getKey() %>: </b><%= claim.getValue().toString() %> <p><b><%= claim.getKey() %>: </b><%= claim.getValue().toString() %>
<% <%
} else {
%>
<p><b>Profile picture: </b><img src="/ldap-portal/picture" />
<%
}
} }
%> %>
<hr /> <hr />

View file

@ -201,6 +201,7 @@ public class LDAPStorageProvider implements UserStorageProvider,
LDAPObject ldapUser = LDAPUtils.addUserToLDAP(this, realm, user); LDAPObject ldapUser = LDAPUtils.addUserToLDAP(this, realm, user);
LDAPUtils.checkUuid(ldapUser, ldapIdentityStore.getConfig()); LDAPUtils.checkUuid(ldapUser, ldapIdentityStore.getConfig());
user.setSingleAttribute(LDAPConstants.LDAP_ID, ldapUser.getUuid()); user.setSingleAttribute(LDAPConstants.LDAP_ID, ldapUser.getUuid());
user.setSingleAttribute(LDAPConstants.LDAP_ENTRY_DN, ldapUser.getDn().toString());
return proxy(realm, user, ldapUser); return proxy(realm, user, ldapUser);
} }
@ -421,6 +422,7 @@ public class LDAPStorageProvider implements UserStorageProvider,
String userDN = ldapUser.getDn().toString(); String userDN = ldapUser.getDn().toString();
imported.setFederationLink(model.getId()); imported.setFederationLink(model.getId());
imported.setSingleAttribute(LDAPConstants.LDAP_ID, ldapUser.getUuid()); imported.setSingleAttribute(LDAPConstants.LDAP_ID, ldapUser.getUuid());
imported.setSingleAttribute(LDAPConstants.LDAP_ENTRY_DN, userDN);
logger.debugf("Imported new user from LDAP to Keycloak DB. Username: [%s], Email: [%s], LDAP_ID: [%s], LDAP Entry DN: [%s]", imported.getUsername(), imported.getEmail(), logger.debugf("Imported new user from LDAP to Keycloak DB. Username: [%s], Email: [%s], LDAP_ID: [%s], LDAP Entry DN: [%s]", imported.getUsername(), imported.getEmail(),
ldapUser.getUuid(), userDN); ldapUser.getUuid(), userDN);

View file

@ -56,6 +56,7 @@ import java.util.Arrays;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -66,7 +67,15 @@ public class LDAPMultipleAttributesTest {
protected String APP_SERVER_BASE_URL = "http://localhost:8081"; protected String APP_SERVER_BASE_URL = "http://localhost:8081";
protected String LOGIN_URL = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(APP_SERVER_BASE_URL + "/auth")).build("test").toString(); protected String LOGIN_URL = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(APP_SERVER_BASE_URL + "/auth")).build("test").toString();
private static LDAPRule ldapRule = new LDAPRule();
// Skip this test on MSAD due to lack of supported user multivalued attributes
private static LDAPRule ldapRule = new LDAPRule((Map<String, String> ldapConfig) -> {
String vendor = ldapConfig.get(LDAPConstants.VENDOR);
return (vendor.equals(LDAPConstants.VENDOR_ACTIVE_DIRECTORY));
});
private static ComponentModel ldapModel = null; private static ComponentModel ldapModel = null;

View file

@ -36,6 +36,11 @@ public class KerberosRule extends LDAPRule {
private final String configLocation; private final String configLocation;
public KerberosRule(String configLocation) { public KerberosRule(String configLocation) {
this(configLocation, null);
}
public KerberosRule(String configLocation, LDAPRuleCondition condition) {
super(condition);
this.configLocation = configLocation; this.configLocation = configLocation;
// Global kerberos configuration // Global kerberos configuration

View file

@ -17,7 +17,10 @@
package org.keycloak.testsuite.rule; package org.keycloak.testsuite.rule;
import org.junit.rules.ExternalResource; import org.jboss.logging.Logger;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.keycloak.testsuite.federation.ldap.LDAPTestConfiguration; import org.keycloak.testsuite.federation.ldap.LDAPTestConfiguration;
import org.keycloak.util.ldap.LDAPEmbeddedServer; import org.keycloak.util.ldap.LDAPEmbeddedServer;
@ -25,28 +28,76 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
/** /**
* This rule handles:
* - Reading of LDAP configuration from properties file
* - Eventually start+stop of LDAP embedded server.
* - Eventually allows to ignore the test if particular condition is not met. This allows to run specific tests just for some LDAP vendors
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class LDAPRule extends ExternalResource { public class LDAPRule implements TestRule {
private static final Logger logger = Logger.getLogger(LDAPRule.class);
public static final String LDAP_CONNECTION_PROPERTIES_LOCATION = "ldap/ldap-connection.properties"; public static final String LDAP_CONNECTION_PROPERTIES_LOCATION = "ldap/ldap-connection.properties";
protected LDAPTestConfiguration ldapTestConfiguration; protected LDAPTestConfiguration ldapTestConfiguration;
protected LDAPEmbeddedServer ldapEmbeddedServer; protected LDAPEmbeddedServer ldapEmbeddedServer;
private final LDAPRuleCondition condition;
public LDAPRule() {
this(null);
}
public LDAPRule(LDAPRuleCondition condition) {
this.condition = condition;
}
@Override @Override
protected void before() throws Throwable { public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
boolean skipTest = before();
if (skipTest) {
logger.infof("Skip %s due to LDAPRuleCondition not met", description.getDisplayName());
return;
}
try {
base.evaluate();
} finally {
after();
}
}
};
}
// Return true if test should be skipped
protected boolean before() throws Throwable {
String connectionPropsLocation = getConnectionPropertiesLocation(); String connectionPropsLocation = getConnectionPropertiesLocation();
ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(connectionPropsLocation); ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(connectionPropsLocation);
if (condition != null && condition.skipTest(ldapTestConfiguration.getLDAPConfig())) {
return true;
}
if (ldapTestConfiguration.isStartEmbeddedLdapLerver()) { if (ldapTestConfiguration.isStartEmbeddedLdapLerver()) {
ldapEmbeddedServer = createServer(); ldapEmbeddedServer = createServer();
ldapEmbeddedServer.init(); ldapEmbeddedServer.init();
ldapEmbeddedServer.start(); ldapEmbeddedServer.start();
} }
return false;
} }
@Override
protected void after() { protected void after() {
try { try {
if (ldapEmbeddedServer != null) { if (ldapEmbeddedServer != null) {
@ -78,4 +129,12 @@ public class LDAPRule extends ExternalResource {
public int getSleepTime() { public int getSleepTime() {
return ldapTestConfiguration.getSleepTime(); return ldapTestConfiguration.getSleepTime();
} }
// Allows to skip particular LDAP test just under specific conditions (eg. some test running just on Active Directory)
public interface LDAPRuleCondition {
boolean skipTest(Map<String, String> ldapConfig);
}
} }

View file

@ -60,6 +60,9 @@ log4j.logger.org.keycloak.connections.jpa.HibernateStatsReporter=debug
# Enable to view ldap logging # Enable to view ldap logging
# log4j.logger.org.keycloak.storage.ldap=trace # log4j.logger.org.keycloak.storage.ldap=trace
# Enable to view queries to LDAP
# log4j.logger.org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore=trace
# Enable to view kerberos/spnego logging # Enable to view kerberos/spnego logging
# log4j.logger.org.keycloak.federation.kerberos=trace # log4j.logger.org.keycloak.federation.kerberos=trace