Allow mapping of UserSessionNotes into UserInfo

Fixes #15369
This commit is contained in:
Simon Levermann 2022-11-07 10:51:41 +01:00 committed by Marek Posolda
parent a56b38c5a6
commit 96c1cf3c49
4 changed files with 30 additions and 13 deletions

View file

@ -38,7 +38,7 @@ import java.util.Map;
* *
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, OIDCAccessTokenResponseMapper { public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, OIDCAccessTokenResponseMapper, UserInfoTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -102,6 +102,13 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
String userSessionNote, String userSessionNote,
String tokenClaimName, String jsonType, String tokenClaimName, String jsonType,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken) {
return createClaimMapper(name, userSessionNote, tokenClaimName, jsonType, accessToken, idToken, false);
}
public static ProtocolMapperModel createClaimMapper(String name,
String userSessionNote,
String tokenClaimName, String jsonType,
boolean accessToken, boolean idToken, boolean userInfo) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocolMapper(PROVIDER_ID);
@ -112,6 +119,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
config.put(OIDCAttributeMapperHelper.JSON_TYPE, jsonType); config.put(OIDCAttributeMapperHelper.JSON_TYPE, jsonType);
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }

View file

@ -17,13 +17,10 @@
package org.keycloak.testsuite.federation.kerberos; package org.keycloak.testsuite.federation.kerberos;
import java.net.URI;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSCredential;
@ -40,16 +37,16 @@ import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.UserInfo;
import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation;
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.testsuite.ActionURIUtils;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.annotation.DisableFeature; import org.keycloak.testsuite.arquillian.annotation.DisableFeature;
import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.AccountHelper;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.TestAppHelper; import org.keycloak.testsuite.util.TestAppHelper;
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId; import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
@ -186,7 +183,7 @@ public abstract class AbstractKerberosSingleRealmTest extends AbstractKerberosTe
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, KerberosConstants.GSS_DELEGATION_CREDENTIAL,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String", KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
true, false); true, false, true);
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper); ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app"); ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep); Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep);
@ -194,7 +191,8 @@ public abstract class AbstractKerberosSingleRealmTest extends AbstractKerberosTe
response.close(); response.close();
// SPNEGO login // SPNEGO login
AccessToken token = assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret"); OAuthClient.AccessTokenResponse tokenResponse = assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
// Assert kerberos ticket in the accessToken can be re-used to authenticate against other 3rd party kerberos service (ApacheDS Server in this case) // 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); String serializedGssCredential = (String) token.getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL);
@ -203,6 +201,12 @@ public abstract class AbstractKerberosSingleRealmTest extends AbstractKerberosTe
String ldapResponse = invokeLdap(gssCredential, token.getPreferredUsername()); String ldapResponse = invokeLdap(gssCredential, token.getPreferredUsername());
Assert.assertEquals("Horatio Nelson", ldapResponse); Assert.assertEquals("Horatio Nelson", ldapResponse);
// Assert kerberos ticket also in userinfo endpoint
UserInfo userInfo = oauth.doUserInfoRequest(tokenResponse.getAccessToken());
Assert.assertEquals(serializedGssCredential, userInfo.getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL));
// Clear USER_INFO_REQUEST event
events.poll();
// Logout // Logout
oauth.openLogout(); oauth.openLogout();
events.poll(); events.poll();
@ -211,8 +215,11 @@ public abstract class AbstractKerberosSingleRealmTest extends AbstractKerberosTe
clientResource.getProtocolMappers().delete(protocolMapperId); clientResource.getProtocolMappers().delete(protocolMapperId);
// Login and assert delegated credential not anymore // Login and assert delegated credential not anymore
token = assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret"); tokenResponse = assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
token = oauth.verifyToken(tokenResponse.getAccessToken());
Assert.assertFalse(token.getOtherClaims().containsKey(KerberosConstants.GSS_DELEGATION_CREDENTIAL)); Assert.assertFalse(token.getOtherClaims().containsKey(KerberosConstants.GSS_DELEGATION_CREDENTIAL));
userInfo = oauth.doUserInfoRequest(tokenResponse.getAccessToken());
Assert.assertFalse(userInfo.getOtherClaims().containsKey(KerberosConstants.GSS_DELEGATION_CREDENTIAL));
events.clear(); events.clear();
} }

View file

@ -179,11 +179,11 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
// } // }
protected AccessToken assertSuccessfulSpnegoLogin(String loginUsername, String expectedUsername, String password) throws Exception { protected OAuthClient.AccessTokenResponse assertSuccessfulSpnegoLogin(String loginUsername, String expectedUsername, String password) throws Exception {
return assertSuccessfulSpnegoLogin("kerberos-app", loginUsername, expectedUsername, password); return assertSuccessfulSpnegoLogin("kerberos-app", loginUsername, expectedUsername, password);
} }
protected AccessToken assertSuccessfulSpnegoLogin(String clientId, String loginUsername, String expectedUsername, String password) throws Exception { protected OAuthClient.AccessTokenResponse assertSuccessfulSpnegoLogin(String clientId, String loginUsername, String expectedUsername, String password) throws Exception {
oauth.clientId(clientId); oauth.clientId(clientId);
Response spnegoResponse = spnegoLogin(loginUsername, password); Response spnegoResponse = spnegoLogin(loginUsername, password);
Assert.assertEquals(302, spnegoResponse.getStatus()); Assert.assertEquals(302, spnegoResponse.getStatus());
@ -206,7 +206,7 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
oauth.idTokenHint(tokenResponse.getIdToken()); oauth.idTokenHint(tokenResponse.getIdToken());
return token; return tokenResponse;
} }

View file

@ -29,6 +29,7 @@ import org.keycloak.storage.ldap.kerberos.LDAPProviderKerberosConfig;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.util.KerberosRule; import org.keycloak.testsuite.util.KerberosRule;
import org.keycloak.testsuite.KerberosEmbeddedServer; import org.keycloak.testsuite.KerberosEmbeddedServer;
import org.keycloak.testsuite.util.OAuthClient;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -67,7 +68,8 @@ public class KerberosLdapCrossRealmTrustTest extends AbstractKerberosTest {
@Test @Test
public void test01SpnegoLoginCRTSuccess() throws Exception { public void test01SpnegoLoginCRTSuccess() throws Exception {
// Login as user from realm KC2.COM . Realm KEYCLOAK.ORG will trust us // Login as user from realm KC2.COM . Realm KEYCLOAK.ORG will trust us
AccessToken token = assertSuccessfulSpnegoLogin("hnelson2@KC2.COM", "hnelson2", "secret"); OAuthClient.AccessTokenResponse tokenResponse = assertSuccessfulSpnegoLogin("hnelson2@KC2.COM", "hnelson2", "secret");
AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
Assert.assertEquals(token.getEmail(), "hnelson2@kc2.com"); Assert.assertEquals(token.getEmail(), "hnelson2@kc2.com");
assertUser("hnelson2", "hnelson2@kc2.com", "Horatio", "Nelson", false); assertUser("hnelson2", "hnelson2@kc2.com", "Horatio", "Nelson", false);