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>
*/
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<>();
@ -102,6 +102,13 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
String userSessionNote,
String tokenClaimName, String jsonType,
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();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
@ -112,6 +119,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
config.put(OIDCAttributeMapperHelper.JSON_TYPE, jsonType);
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
mapper.setConfig(config);
return mapper;
}

View file

@ -17,13 +17,10 @@
package org.keycloak.testsuite.federation.kerberos;
import java.net.URI;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.ietf.jgss.GSSCredential;
@ -40,16 +37,16 @@ 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.UserInfo;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.testsuite.ActionURIUtils;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.annotation.DisableFeature;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.AccountHelper;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.TestAppHelper;
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,
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
true, false);
true, false, true);
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep);
@ -194,7 +191,8 @@ public abstract class AbstractKerberosSingleRealmTest extends AbstractKerberosTe
response.close();
// 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)
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());
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
oauth.openLogout();
events.poll();
@ -211,8 +215,11 @@ public abstract class AbstractKerberosSingleRealmTest extends AbstractKerberosTe
clientResource.getProtocolMappers().delete(protocolMapperId);
// 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));
userInfo = oauth.doUserInfoRequest(tokenResponse.getAccessToken());
Assert.assertFalse(userInfo.getOtherClaims().containsKey(KerberosConstants.GSS_DELEGATION_CREDENTIAL));
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);
}
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);
Response spnegoResponse = spnegoLogin(loginUsername, password);
Assert.assertEquals(302, spnegoResponse.getStatus());
@ -206,7 +206,7 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
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.util.KerberosRule;
import org.keycloak.testsuite.KerberosEmbeddedServer;
import org.keycloak.testsuite.util.OAuthClient;
import javax.ws.rs.core.Response;
@ -67,7 +68,8 @@ public class KerberosLdapCrossRealmTrustTest extends AbstractKerberosTest {
@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");
OAuthClient.AccessTokenResponse tokenResponse = assertSuccessfulSpnegoLogin("hnelson2@KC2.COM", "hnelson2", "secret");
AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
Assert.assertEquals(token.getEmail(), "hnelson2@kc2.com");
assertUser("hnelson2", "hnelson2@kc2.com", "Horatio", "Nelson", false);