LDAP polishing
This commit is contained in:
parent
ac00f7fee2
commit
4b6df5d489
8 changed files with 89 additions and 23 deletions
|
@ -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"
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue