Enhancing Light Weight Token(#22148)

Closes #21183
This commit is contained in:
shigeyuki kabano 2023-08-01 18:52:03 +09:00 committed by Marek Posolda
parent c51060ae34
commit 6112b25648
50 changed files with 1091 additions and 304 deletions

View file

@ -40,6 +40,9 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class JsonWebToken implements Serializable, Token {
public static final String AZP = "azp";
public static final String SUBJECT = "sub";
@JsonProperty("jti")
protected String id;
@ -53,11 +56,11 @@ public class JsonWebToken implements Serializable, Token {
@JsonSerialize(using = StringOrArraySerializer.class)
@JsonDeserialize(using = StringOrArrayDeserializer.class)
protected String[] audience;
@JsonProperty("sub")
@JsonProperty(SUBJECT)
protected String subject;
@JsonProperty("typ")
protected String type;
@JsonProperty("azp")
@JsonProperty(AZP)
public String issuedFor;
protected Map<String, Object> otherClaims = new HashMap<>();

View file

@ -3096,6 +3096,10 @@
"label": "Add to userinfo",
"tooltip": "Should the claim be added to the userinfo?"
},
"includeInIntrospection": {
"label": "Add to token introspection",
"tooltip": "Should the claim be added to the token introspection?"
},
"sectorIdentifierUri": {
"label": "Sector Identifier URI",
"tooltip": "Providers that use pairwise sub values and support Dynamic Client Registration SHOULD use the sector_identifier_uri parameter. It provides a way for a group of websites under common administrative control to have consistent pairwise sub values independent of the individual domain names. It also provides a way for Clients to change redirect_uri domains without having to reregister all their users."

View file

@ -315,6 +315,7 @@ public class UserSessionAdapter implements UserSessionModel {
update(task);
}
@Override
public SessionPersistenceState getPersistenceState() {
return persistenceState;
}

View file

@ -232,6 +232,11 @@ public class PersistentUserSessionAdapter implements OfflineUserSessionModel {
return State.valueOf(state);
}
@Override
public SessionPersistenceState getPersistenceState() {
return SessionPersistenceState.PERSISTENT;
}
@Override
public void setState(State state) {
String stateStr = state==null ? null : state.toString();

View file

@ -23,7 +23,6 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelIllegalStateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.map.common.TimeAdapter;
@ -289,4 +288,9 @@ public class MapUserSessionAdapter extends AbstractUserSessionModel {
public int hashCode() {
return getId().hashCode();
}
@Override
public SessionPersistenceState getPersistenceState() {
return entity.getPersistenceState();
}
}

View file

@ -135,6 +135,8 @@ public interface UserSessionModel {
}
}
SessionPersistenceState getPersistenceState();
/**
* Flag used when creating user session
*/

View file

@ -24,6 +24,12 @@ import org.keycloak.TokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.crypto.SignatureProvider;
import org.keycloak.crypto.SignatureVerifierContext;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.ImpersonationSessionNote;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@ -31,6 +37,8 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.Urls;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.services.util.UserSessionUtil;
import org.keycloak.util.JsonSerialization;
import jakarta.ws.rs.core.MediaType;
@ -55,6 +63,7 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
public Response introspect(String token) {
try {
AccessToken accessToken = verifyAccessToken(token);
accessToken = transformAccessToken(accessToken);
ObjectNode tokenMetadata;
if (accessToken != null) {
@ -106,6 +115,57 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
}
}
private AccessToken transformAccessToken(AccessToken token) {
if (token == null) {
return null;
}
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
EventBuilder event = new EventBuilder(realm, session, session.getContext().getConnection())
.event(EventType.INTROSPECT_TOKEN)
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN);
UserSessionModel userSession;
try {
userSession = UserSessionUtil.findValidSession(session, realm, token, event, client);
} catch (Exception e) {
logger.debugf("Can not get user session: %s", e.getMessage());
// Backwards compatibility
return token;
}
if (userSession.getUser() == null) {
logger.debugf("User not found");
// Backwards compatibility
return token;
}
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession, session);
AccessToken smallToken = getAccessTokenFromStoredData(token, userSession);
return tokenManager.transformIntrospectionAccessToken(session, smallToken, userSession, clientSessionCtx);
}
private AccessToken getAccessTokenFromStoredData(AccessToken token, UserSessionModel userSession) {
// Copy just "basic" claims from the initial token. The same like filled in TokenManager.initToken. The rest should be possibly added by protocol mappers (only if configured for introspection response)
AccessToken newToken = new AccessToken();
newToken.id(token.getId());
newToken.type(token.getType());
newToken.subject(token.getSubject() != null ? token.getSubject() : userSession.getUser().getId());
newToken.iat(token.getIat());
newToken.exp(token.getExp());
newToken.issuedFor(token.getIssuedFor());
newToken.issuer(token.getIssuer());
newToken.setNonce(token.getNonce());
newToken.setScope(token.getScope());
newToken.setAuth_time(token.getAuth_time());
newToken.setSessionState(token.getSessionState());
// In the case of a refresh token, aud is a basic claim.
newToken.audience(token.getAudience());
// The cnf is not a claim controlled by the protocol mapper.
newToken.setConfirmation(token.getConfirmation());
return newToken;
}
protected AccessToken verifyAccessToken(String token) {
AccessToken accessToken;

View file

@ -137,25 +137,25 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
model = UserAttributeMapper.createClaimMapper(USERNAME,
"username",
"preferred_username", String.class.getSimpleName(),
true, true);
true, true, true);
builtins.put(USERNAME, model);
model = UserAttributeMapper.createClaimMapper(EMAIL,
"email",
"email", "String",
true, true);
true, true, true);
builtins.put(EMAIL, model);
model = UserAttributeMapper.createClaimMapper(GIVEN_NAME,
"firstName",
"given_name", "String",
true, true);
true, true, true);
builtins.put(GIVEN_NAME, model);
model = UserAttributeMapper.createClaimMapper(FAMILY_NAME,
"lastName",
"family_name", "String",
true, true);
true, true, true);
builtins.put(FAMILY_NAME, model);
createUserAttributeMapper(MIDDLE_NAME, "middleName", IDToken.MIDDLE_NAME, "String");
@ -175,10 +175,10 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED,
"emailVerified",
"email_verified", "boolean",
true, true);
true, true, true);
builtins.put(EMAIL_VERIFIED, model);
ProtocolMapperModel fullName = FullNameMapper.create(FULL_NAME, true, true, true);
ProtocolMapperModel fullName = FullNameMapper.create(FULL_NAME, true, true, true, true);
builtins.put(FULL_NAME, fullName);
ProtocolMapperModel address = AddressMapper.createAddressMapper();
@ -187,19 +187,19 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
true, false);
true, false, true);
builtins.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, model);
model = UserRealmRoleMappingMapper.create(null, REALM_ROLES, "realm_access.roles", true, false, true);
model = UserRealmRoleMappingMapper.create(null, REALM_ROLES, "realm_access.roles", true, false, true, true);
builtins.put(REALM_ROLES, model);
model = UserClientRoleMappingMapper.create(null, null, CLIENT_ROLES, "resource_access.${client_id}.roles", true, false, true);
model = UserClientRoleMappingMapper.create(null, null, CLIENT_ROLES, "resource_access.${client_id}.roles", true, false, true, true);
builtins.put(CLIENT_ROLES, model);
model = AudienceResolveProtocolMapper.createClaimMapper(AUDIENCE_RESOLVE);
model = AudienceResolveProtocolMapper.createClaimMapper(AUDIENCE_RESOLVE, true, true);
builtins.put(AUDIENCE_RESOLVE, model);
model = AllowedWebOriginsProtocolMapper.createClaimMapper(ALLOWED_WEB_ORIGINS);
model = AllowedWebOriginsProtocolMapper.createClaimMapper(ALLOWED_WEB_ORIGINS, true, true);
builtins.put(ALLOWED_WEB_ORIGINS, model);
builtins.put(IMPERSONATOR_ID.getDisplayName(), UserSessionNoteMapper.createUserSessionNoteMapper(IMPERSONATOR_ID));
@ -207,14 +207,14 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
model = UserAttributeMapper.createClaimMapper(UPN, "username",
"upn", "String",
true, true);
true, true, true);
builtins.put(UPN, model);
model = UserRealmRoleMappingMapper.create(null, GROUPS, GROUPS, true, true, true);
model = UserRealmRoleMappingMapper.create(null, GROUPS, GROUPS, true, true, true, true);
builtins.put(GROUPS, model);
if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
model = AcrProtocolMapper.create(ACR, true, true);
model = AcrProtocolMapper.create(ACR, true, true, true);
builtins.put(ACR, model);
}
}
@ -223,7 +223,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
ProtocolMapperModel model = UserAttributeMapper.createClaimMapper(name,
attrName,
claimName, type,
true, true, false);
true, true, true, false);
builtins.put(name, model);
}

View file

@ -61,6 +61,7 @@ import org.keycloak.models.utils.SessionExpirationUtils;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.mappers.TokenIntrospectionTokenMapper;
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenResponseMapper;
import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper;
@ -785,6 +786,17 @@ public class TokenManager {
});
}
public AccessToken transformIntrospectionAccessToken(KeycloakSession session, AccessToken token,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
return ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx, mapper -> mapper.getValue() instanceof TokenIntrospectionTokenMapper)
.collect(new TokenCollector<AccessToken>(token) {
@Override
protected AccessToken applyMapper(AccessToken token, Map.Entry<ProtocolMapperModel, ProtocolMapper> mapper) {
return ((TokenIntrospectionTokenMapper) mapper.getValue()).transformIntrospectionToken(token, mapper.getKey(), session, userSession, clientSessionCtx);
}
});
}
public Map<String, Object> generateUserInfoClaims(AccessToken userInfo, UserModel userModel) {
Map<String, Object> claims = new HashMap<>();
claims.put("sub", userInfo.getSubject() == null? userModel.getId() : userInfo.getSubject());

View file

@ -24,7 +24,6 @@ import org.keycloak.TokenVerifier;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.Profile;
import org.keycloak.common.VerificationException;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.crypto.ContentEncryptionProvider;
import org.keycloak.crypto.CekManagementProvider;
import org.keycloak.crypto.KeyWrapper;
@ -48,7 +47,6 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
@ -59,15 +57,11 @@ import org.keycloak.services.Urls;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.UserInfoRequestContext;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.UserSessionCrossDCManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.util.DPoPUtil;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.services.util.MtlsHoKTokenUtil;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel;
import org.keycloak.services.util.UserSessionUtil;
import org.keycloak.util.JsonSerialization;
import org.keycloak.util.TokenUtil;
import org.keycloak.utils.MediaType;
@ -229,7 +223,7 @@ public class UserInfoEndpoint {
throw error.invalidToken("Client disabled");
}
UserSessionModel userSession = findValidSession(token, event, clientModel);
UserSessionModel userSession = UserSessionUtil.findValidSession(session, realm, token, event, clientModel);
UserModel userModel = userSession.getUser();
if (userModel == null) {
@ -350,73 +344,6 @@ public class UserInfoEndpoint {
return encryptedToken;
}
private UserSessionModel createTransientSessionForClient(AccessToken token, ClientModel client) {
// create a transient session
UserModel user = TokenManager.lookupUserFromStatelessToken(session, realm, token);
if (user == null) {
throw error.invalidToken("User not found");
}
UserSessionModel userSession = new UserSessionManager(session).createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), clientConnection.getRemoteAddr(),
ServiceAccountConstants.CLIENT_AUTH, false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
// attach an auth session for the client
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(realm);
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
authSession.setAuthenticatedUser(userSession.getUser());
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
AuthenticationManager.setClientScopesInSession(authSession);
TokenManager.attachAuthenticationSession(session, userSession, authSession);
return userSession;
}
private UserSessionModel findValidSession(AccessToken token, EventBuilder event, ClientModel client) {
if (token.getSessionState() == null) {
return createTransientSessionForClient(token, client);
}
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
UserSessionModel offlineUserSession = null;
if (AuthenticationManager.isSessionValid(realm, userSession)) {
checkTokenIssuedAt(token, userSession, event, client);
event.session(userSession);
return userSession;
} else {
offlineUserSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
if (AuthenticationManager.isOfflineSessionValid(realm, offlineUserSession)) {
checkTokenIssuedAt(token, offlineUserSession, event, client);
event.session(offlineUserSession);
return offlineUserSession;
}
}
if (userSession == null && offlineUserSession == null) {
event.error(Errors.USER_SESSION_NOT_FOUND);
throw error.invalidToken("User session not found or doesn't have client attached on it");
}
if (userSession != null) {
event.session(userSession);
} else {
event.session(offlineUserSession);
}
event.error(Errors.SESSION_EXPIRED);
throw error.invalidToken("Session expired");
}
private void checkTokenIssuedAt(AccessToken token, UserSessionModel userSession, EventBuilder event, ClientModel client) {
if (token.isIssuedBeforeSessionStart(userSession.getStarted())) {
event.error(Errors.INVALID_TOKEN);
throw error.invalidToken("Stale token");
}
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (token.isIssuedBeforeSessionStart(clientSession.getStarted())) {
event.error(Errors.INVALID_TOKEN);
throw error.invalidToken("Stale token");
}
}
private void checkAccessTokenDuplicated(MultivaluedMap<String, String> formParams) {
// If access_token is not provided, error is thrown in issueUserInfo().
// Only checks duplication of access token parameter in this function.

View file

@ -106,6 +106,17 @@ public abstract class AbstractOIDCProtocolMapper implements ProtocolMapper {
return accessTokenResponse;
}
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!OIDCAttributeMapperHelper.includeInIntrospection(mappingModel)){
return token;
}
setClaim(token, mappingModel, userSession, session, clientSessionCtx);
return token;
}
/**
* Intended to be overridden in {@link ProtocolMapper} implementations to add claims to an token.
* @param token

View file

@ -14,6 +14,7 @@ import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@ -22,7 +23,7 @@ import java.util.List;
*
* @author <a href="mailto:martin.hardselius@gmail.com">Martin Hardselius</a>
*/
public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
public static final String PROVIDER_ID_SUFFIX = "-pairwise-sub-mapper";
public abstract String getIdPrefix();
@ -75,6 +76,12 @@ public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapp
return token;
}
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
setAccessTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSessionCtx.getClientSession().getClient(), mappingModel), userSession.getUser().getId()));
return token;
}
@Override
public AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
setUserInfoTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSessionCtx.getClientSession().getClient(), mappingModel), userSession.getUser().getId()));

View file

@ -38,7 +38,7 @@ import java.util.stream.Collectors;
*
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
*/
abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
@Override
public int getPriority() {

View file

@ -42,7 +42,7 @@ import org.keycloak.services.managers.AuthenticationManager;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, EnvironmentDependentProviderFactory {
public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory {
private static final Logger logger = Logger.getLogger(AcrProtocolMapper.class);
@ -87,7 +87,7 @@ public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OID
token.setAcr(acr);
}
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken) {
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
@ -95,6 +95,7 @@ public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OID
Map<String, String> config = new HashMap<>();
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config);
return mapper;
}

View file

@ -35,7 +35,7 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
@ -69,10 +69,10 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
public static final String PROVIDER_ID = "oidc-address-mapper";
public static ProtocolMapperModel createAddressMapper() {
return createAddressMapper(true, true, true);
return createAddressMapper(true, true, true, true);
}
public static ProtocolMapperModel createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo) {
public static ProtocolMapperModel createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo, boolean introspectionEndpoint) {
Map<String, String> config;
ProtocolMapperModel address = new ProtocolMapperModel();
address.setName("address");
@ -82,6 +82,7 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, Boolean.toString(accessToken));
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, Boolean.toString(idToken));
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.toString(userInfo));
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, Boolean.toString(introspectionEndpoint));
config.put(getModelPropertyName(STREET), STREET);
config.put(getModelPropertyName(AddressClaimSet.LOCALITY), AddressClaimSet.LOCALITY);

View file

@ -19,7 +19,9 @@ package org.keycloak.protocol.oidc.mappers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.keycloak.models.ClientModel;
@ -32,18 +34,24 @@ import org.keycloak.protocol.oidc.utils.WebOriginsUtils;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION;
/**
* Protocol mapper to add allowed web origins to the access token to the 'allowed-origins' claim
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AllowedWebOriginsProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
public class AllowedWebOriginsProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
public static final String PROVIDER_ID = "oidc-allowed-origins-mapper";
static {
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AllowedWebOriginsProtocolMapper.class);
}
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
@ -72,23 +80,72 @@ public class AllowedWebOriginsProtocolMapper extends AbstractOIDCProtocolMapper
@Override
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!includeInAccessToken(mappingModel)){
return token;
}
setWebOrigin(token, session, clientSessionCtx);
return token;
}
private boolean includeInAccessToken(ProtocolMapperModel mappingModel) {
String includeInAccessToken = mappingModel.getConfig().get(INCLUDE_IN_ACCESS_TOKEN);
// Backwards compatibility
if (includeInAccessToken == null) {
return true;
}
return "true".equals(includeInAccessToken);
}
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!includeInIntrospection(mappingModel)) {
return token;
}
setWebOrigin(token, session, clientSessionCtx);
return token;
}
private boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
// Backwards compatibility
if (includeInIntrospection == null) {
return true;
}
return "true".equals(includeInIntrospection);
}
private void setWebOrigin(AccessToken token, KeycloakSession session, ClientSessionContext clientSessionCtx) {
ClientModel client = clientSessionCtx.getClientSession().getClient();
Set<String> allowedOrigins = client.getWebOrigins();
if (allowedOrigins != null && !allowedOrigins.isEmpty()) {
token.setAllowedOrigins(WebOriginsUtils.resolveValidWebOrigins(session, client));
}
return token;
}
public static ProtocolMapperModel createClaimMapper(String name) {
public static ProtocolMapperModel createClaimMapper(String name, boolean accessToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
mapper.setConfig(Collections.emptyMap());
Map<String, String> config = new HashMap<>();
if (accessToken) {
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
} else {
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
}
if (introspectionEndpoint) {
config.put(INCLUDE_IN_INTROSPECTION, "true");
} else {
config.put(INCLUDE_IN_INTROSPECTION, "false");
}
mapper.setConfig(config);
return mapper;
}

View file

@ -33,7 +33,7 @@ import org.keycloak.representations.IDToken;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -115,7 +115,7 @@ public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implement
public static ProtocolMapperModel createClaimMapper(String name,
String includedClientAudience,
String includedCustomAudience,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
@ -131,6 +131,7 @@ public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implement
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config);
return mapper;
}

View file

@ -19,6 +19,7 @@ package org.keycloak.protocol.oidc.mappers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -32,16 +33,21 @@ import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.utils.RoleResolveUtil;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION;
/**
* Protocol mapper, which adds all client_ids of "allowed" clients to the audience field of the token. Allowed client means the client
* for which user has at least one client role
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AudienceResolveProtocolMapper.class);
}
public static final String PROVIDER_ID = "oidc-audience-resolve-mapper";
@ -79,6 +85,46 @@ public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper im
@Override
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!includeInAccessToken(mappingModel)){
return token;
}
setAudience(token, clientSessionCtx, session);
return token;
}
private boolean includeInAccessToken(ProtocolMapperModel mappingModel) {
String includeInAccessToken = mappingModel.getConfig().get(INCLUDE_IN_ACCESS_TOKEN);
// Backwards compatibility
if (includeInAccessToken == null) {
return true;
}
return "true".equals(includeInAccessToken);
}
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!includeInIntrospection(mappingModel)) {
return token;
}
setAudience(token, clientSessionCtx, session);
return token;
}
private boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
// Backwards compatibility
if (includeInIntrospection == null) {
return true;
}
return "true".equals(includeInIntrospection);
}
private void setAudience(AccessToken token, ClientSessionContext clientSessionCtx, KeycloakSession session) {
String clientId = clientSessionCtx.getClientSession().getClient().getClientId();
for (Map.Entry<String, AccessToken.Access> entry : RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx).entrySet()) {
@ -92,16 +138,25 @@ public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper im
token.addAudience(entry.getKey());
}
}
return token;
}
public static ProtocolMapperModel createClaimMapper(String name) {
public static ProtocolMapperModel createClaimMapper(String name, boolean accessToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
mapper.setConfig(Collections.emptyMap());
Map<String, String> config = new HashMap<>();
if (accessToken) {
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
} else {
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
}
if (introspectionEndpoint) {
config.put(INCLUDE_IN_INTROSPECTION, "true");
} else {
config.put(INCLUDE_IN_INTROSPECTION, "false");
}
mapper.setConfig(config);
return mapper;
}
}

View file

@ -110,16 +110,16 @@ public class ClaimsParameterTokenMapper extends AbstractOIDCProtocolMapper imple
fullNameMapper.setClaim(token, mappingModel, userSession);
} else if (i.equals(IDToken.GIVEN_NAME)) {
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested firstName", "firstName", IDToken.GIVEN_NAME, "String", false, true), userSession);
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested firstName", "firstName", IDToken.GIVEN_NAME, "String", false, true, false), userSession);
} else if (i.equals(IDToken.FAMILY_NAME)) {
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested lastName", "lastName", IDToken.FAMILY_NAME, "String", false, true), userSession);
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested lastName", "lastName", IDToken.FAMILY_NAME, "String", false, true, false), userSession);
} else if (i.equals(IDToken.PREFERRED_USERNAME)) {
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested username", "username", IDToken.PREFERRED_USERNAME, "String", false, true), userSession);
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested username", "username", IDToken.PREFERRED_USERNAME, "String", false, true, false), userSession);
} else if (i.equals(IDToken.EMAIL)) {
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested email", "email", IDToken.EMAIL, "String", false, true), userSession);
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested email", "email", IDToken.EMAIL, "String", false, true, false), userSession);
}
});
}

View file

@ -123,7 +123,7 @@ public class ClaimsParameterWithValueIdTokenMapper extends AbstractOIDCProtocolM
}
HardcodedClaim hardcodedClaimMapper = new HardcodedClaim();
hardcodedClaimMapper.setClaim(token, HardcodedClaim.create("hard", claimName, claim, "String", false, true), userSession);
hardcodedClaimMapper.setClaim(token, HardcodedClaim.create("hard", claimName, claim, "String", false, true, false), userSession);
}
public static ProtocolMapperModel createMapper(String name, String attributeValue, boolean idToken) {

View file

@ -37,7 +37,7 @@ import java.util.Optional;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -83,7 +83,7 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
}
}
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo) {
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
@ -92,6 +92,7 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
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");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config);
return mapper;
}

View file

@ -39,7 +39,7 @@ import java.util.stream.Collectors;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
@ -107,7 +107,7 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
public static ProtocolMapperModel create(String name,
String tokenClaimName,
boolean consentRequired, String consentText,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
@ -116,6 +116,7 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config);
return mapper;

View file

@ -38,7 +38,7 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper,
OIDCAccessTokenResponseMapper {
OIDCAccessTokenResponseMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
@ -104,7 +104,7 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
public static ProtocolMapperModel create(String name,
String hardcodedName,
String hardcodedValue, String claimType,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
@ -115,6 +115,7 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
config.put(OIDCAttributeMapperHelper.JSON_TYPE, claimType);
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config);
return mapper;
}

View file

@ -40,7 +40,7 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper {
public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -104,6 +104,14 @@ public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAcc
return token;
}
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
// the mapper is always executed and then other role mappers decide if the claims are really set to the token
setClaim(token, mappingModel, userSession, session, clientSessionCtx);
return token;
}
@Override
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session,
ClientSessionContext clientSessionCtx) {

View file

@ -31,10 +31,17 @@ import org.keycloak.representations.IDToken;
import org.keycloak.services.ServicesLogger;
import org.keycloak.util.JsonSerialization;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@ -60,6 +67,10 @@ public class OIDCAttributeMapperHelper {
public static final String INCLUDE_IN_USERINFO_LABEL = "includeInUserInfo.label";
public static final String INCLUDE_IN_USERINFO_HELP_TEXT = "includeInUserInfo.tooltip";
public static final String INCLUDE_IN_INTROSPECTION = "introspection.token.claim";
public static final String INCLUDE_IN_INTROSPECTION_LABEL = "includeInIntrospection.label";
public static final String INCLUDE_IN_INTROSPECTION_HELP_TEXT = "includeInIntrospection.tooltip";
private static final Logger logger = Logger.getLogger(OIDCAttributeMapperHelper.class);
/**
@ -317,15 +328,15 @@ public class OIDCAttributeMapperHelper {
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
boolean accessToken, boolean idToken,
boolean accessToken, boolean idToken, boolean introspectionEndpoint,
String mapperId) {
return createClaimMapper(name, userAttribute,tokenClaimName, claimType, accessToken, idToken, true, mapperId);
return createClaimMapper(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, true, introspectionEndpoint, mapperId);
}
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, boolean userinfo,
boolean accessToken, boolean idToken, boolean userinfo, boolean introspectionEndpoint,
String mapperId) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
@ -338,6 +349,7 @@ public class OIDCAttributeMapperHelper {
if (accessToken) config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(INCLUDE_IN_ID_TOKEN, "true");
if (userinfo) config.put(INCLUDE_IN_USERINFO, "true");
if (introspectionEndpoint) config.put(INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config);
return mapper;
}
@ -369,6 +381,17 @@ public class OIDCAttributeMapperHelper {
return "true".equals(includeInUserInfo);
}
public static boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
// Backwards compatibility
if (includeInIntrospection == null && includeInAccessToken(mappingModel)) {
return true;
}
return "true".equals(includeInIntrospection);
}
public static void addAttributeConfig(List<ProviderConfigProperty> configProperties, Class<? extends ProtocolMapper> protocolMapperClass) {
addTokenClaimNameConfig(configProperties);
addJsonTypeConfig(configProperties);
@ -441,5 +464,15 @@ public class OIDCAttributeMapperHelper {
property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_RESPONSE_HELP_TEXT);
configProperties.add(property);
}
if (TokenIntrospectionTokenMapper.class.isAssignableFrom(protocolMapperClass)) {
ProviderConfigProperty property = new ProviderConfigProperty();
property.setName(INCLUDE_IN_INTROSPECTION);
property.setLabel(INCLUDE_IN_INTROSPECTION_LABEL);
property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
property.setDefaultValue("true");
property.setHelpText(INCLUDE_IN_INTROSPECTION_HELP_TEXT);
configProperties.add(property);
}
}
}

View file

@ -40,7 +40,7 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper {
public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -111,6 +111,14 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
return token;
}
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
// the mapper is always executed and then other role mappers decide if the claims are really set to the token
setClaim(token, mappingModel, userSession, session, clientSessionCtx);
return token;
}
@Override
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session,
ClientSessionContext clientSessionCtx) {

View file

@ -46,7 +46,7 @@ import java.util.List;
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
*/
public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper,
OIDCAccessTokenResponseMapper, EnvironmentDependentProviderFactory {
OIDCAccessTokenResponseMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory {
public static final String PROVIDER_ID = "oidc-script-based-protocol-mapper";
@ -197,10 +197,10 @@ public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper im
public static ProtocolMapperModel create(String name,
String userAttribute,
String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, String script, boolean multiValued) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint, String script, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
tokenClaimName, claimType,
accessToken, idToken,
accessToken, idToken, introspectionEndpoint,
script);
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));

View file

@ -0,0 +1,12 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.AccessToken;
public interface TokenIntrospectionTokenMapper {
AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx);
}

View file

@ -37,7 +37,7 @@ import java.util.List;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
@ -106,19 +106,19 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, boolean multivalued) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multivalued) {
return createClaimMapper(name, userAttribute, tokenClaimName, claimType,
accessToken, idToken, multivalued, false);
accessToken, idToken, introspectionEndpoint, multivalued, false);
}
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
boolean accessToken, boolean idToken,
boolean accessToken, boolean idToken, boolean introspectionEndpoint,
boolean multivalued, boolean aggregateAttrs) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
tokenClaimName, claimType,
accessToken, idToken,
accessToken, idToken, introspectionEndpoint,
PROVIDER_ID);
if (multivalued) {
@ -134,8 +134,8 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return createClaimMapper(name, userAttribute, tokenClaimName, claimType,
accessToken, idToken, false, false);
accessToken, idToken, introspectionEndpoint, false, false);
}
}

View file

@ -135,18 +135,18 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken) {
return create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, false);
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, false);
}
public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
tokenClaimName, "String",
accessToken, idToken, false,
accessToken, idToken, false, introspectionEndpoint,
PROVIDER_ID);
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));

View file

@ -35,7 +35,7 @@ import java.util.List;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
@ -89,10 +89,10 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
tokenClaimName, claimType,
accessToken, idToken,
accessToken, idToken, introspectionEndpoint,
PROVIDER_ID);
}
}

View file

@ -100,17 +100,17 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
public static ProtocolMapperModel create(String realmRolePrefix,
String name,
String tokenClaimName, boolean accessToken, boolean idToken) {
String tokenClaimName, boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, false);
return create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, false);
}
public static ProtocolMapperModel create(String realmRolePrefix,
String name,
String tokenClaimName, boolean accessToken, boolean idToken, boolean multiValued) {
String tokenClaimName, boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
tokenClaimName, "String",
accessToken, idToken, false,
accessToken, idToken, false, introspectionEndpoint,
PROVIDER_ID);
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));

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, UserInfoTokenMapper {
public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, OIDCAccessTokenResponseMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -101,14 +101,14 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
public static ProtocolMapperModel createClaimMapper(String name,
String userSessionNote,
String tokenClaimName, String jsonType,
boolean accessToken, boolean idToken) {
return createClaimMapper(name, userSessionNote, tokenClaimName, jsonType, accessToken, idToken, false);
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return createClaimMapper(name, userSessionNote, tokenClaimName, jsonType, accessToken, idToken, false, introspectionEndpoint);
}
public static ProtocolMapperModel createClaimMapper(String name,
String userSessionNote,
String tokenClaimName, String jsonType,
boolean accessToken, boolean idToken, boolean userInfo) {
boolean accessToken, boolean idToken, boolean userInfo, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
@ -120,6 +120,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
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");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config);
return mapper;
}
@ -135,7 +136,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
userSessionNoteDescriptor.toString(),
userSessionNoteDescriptor.getTokenClaim(),
"String",
true, true
true, true, true
);
}

View file

@ -170,7 +170,7 @@ public class ClientManager {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER,
ServiceAccountConstants.CLIENT_ID,
ServiceAccountConstants.CLIENT_ID, "String",
true, true);
true, true, true);
client.addProtocolMapper(protocolMapper);
}
@ -180,7 +180,7 @@ public class ClientManager {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_HOST_PROTOCOL_MAPPER,
ServiceAccountConstants.CLIENT_HOST,
ServiceAccountConstants.CLIENT_HOST, "String",
true, true);
true, true, true);
client.addProtocolMapper(protocolMapper);
}
@ -189,7 +189,7 @@ public class ClientManager {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ADDRESS_PROTOCOL_MAPPER,
ServiceAccountConstants.CLIENT_ADDRESS,
ServiceAccountConstants.CLIENT_ADDRESS, "String",
true, true);
true, true, true);
client.addProtocolMapper(protocolMapper);
}
}

View file

@ -0,0 +1,97 @@
package org.keycloak.services.util;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.Urls;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.UserSessionCrossDCManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel;
import org.keycloak.utils.OAuth2Error;
public class UserSessionUtil {
public static UserSessionModel findValidSession(KeycloakSession session, RealmModel realm, AccessToken token, EventBuilder event, ClientModel client) {
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
if (token.getSessionState() == null) {
return createTransientSessionForClient(session, realm, token, client);
}
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
UserSessionModel offlineUserSession = null;
if (AuthenticationManager.isSessionValid(realm, userSession)) {
checkTokenIssuedAt(realm, token, userSession, event, client);
event.session(userSession);
return userSession;
} else {
offlineUserSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
if (AuthenticationManager.isOfflineSessionValid(realm, offlineUserSession)) {
checkTokenIssuedAt(realm, token, offlineUserSession, event, client);
event.session(offlineUserSession);
return offlineUserSession;
}
}
if (userSession == null && offlineUserSession == null) {
event.error(Errors.USER_SESSION_NOT_FOUND);
throw error.invalidToken("User session not found or doesn't have client attached on it");
}
if (userSession != null) {
event.session(userSession);
} else {
event.session(offlineUserSession);
}
event.error(Errors.SESSION_EXPIRED);
throw error.invalidToken("Session expired");
}
private static UserSessionModel createTransientSessionForClient(KeycloakSession session, RealmModel realm, AccessToken token, ClientModel client) {
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
// create a transient session
UserModel user = TokenManager.lookupUserFromStatelessToken(session, realm, token);
if (user == null) {
throw error.invalidToken("User not found");
}
ClientConnection clientConnection = session.getContext().getConnection();
UserSessionModel userSession = new UserSessionManager(session).createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), clientConnection.getRemoteAddr(),
ServiceAccountConstants.CLIENT_AUTH, false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
// attach an auth session for the client
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(realm);
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
authSession.setAuthenticatedUser(userSession.getUser());
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
AuthenticationManager.setClientScopesInSession(authSession);
TokenManager.attachAuthenticationSession(session, userSession, authSession);
return userSession;
}
private static void checkTokenIssuedAt(RealmModel realm, AccessToken token, UserSessionModel userSession, EventBuilder event, ClientModel client) {
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
if (token.isIssuedBeforeSessionStart(userSession.getStarted())) {
event.error(Errors.INVALID_TOKEN);
throw error.invalidToken("Stale token");
}
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (token.isIssuedBeforeSessionStart(clientSession.getStarted())) {
event.error(Errors.INVALID_TOKEN);
throw error.invalidToken("Stale token");
}
}
}

View file

@ -817,7 +817,7 @@ public class TestingResourceProvider implements RealmResourceProvider {
clientScopeModel.setIncludeInTokenScope(true);
// Add audience protocol mapper
ProtocolMapperModel audienceMapper = AudienceProtocolMapper.createClaimMapper("Audience for " + clientId, clientId, null,true, false);
ProtocolMapperModel audienceMapper = AudienceProtocolMapper.createClaimMapper("Audience for " + clientId, clientId, null,true, false, true );
clientScopeModel.addProtocolMapper(audienceMapper);
return clientScopeModel.getId();

View file

@ -36,7 +36,7 @@ public class KcOidcBrokerSubMatchIntrospectionTest extends AbstractBrokerTest {
List<ProtocolMapperRepresentation> mappers = new ArrayList<>();
ProtocolMapperRepresentation hardcodedClaim = createHardcodedClaim("sub-override", "sub", "overriden",
"String", false, false);
"String", false, false, false);
hardcodedClaim.getConfig().put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.TRUE.toString());

View file

@ -243,7 +243,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
ClientRepresentation brokerApp = clients.findByClientId("brokerapp").get(0);
IdentityProviderResource identityProviderResource = getIdentityProviderResource();
clients.get(brokerApp.getId()).getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true)).close();
clients.get(brokerApp.getId()).getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true, true)).close();
IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation();
@ -445,7 +445,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
RealmResource realm = adminClient.realm(bc.providerRealmName());
ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0);
ClientResource clientResource = realm.clients().get(rep.getId());
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "azp", "invalid-azp", ProviderConfigProperty.STRING_TYPE, true, true);
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "azp", "invalid-azp", ProviderConfigProperty.STRING_TYPE, true, true, true);
clientResource.getProtocolMappers().createMapper(hardCodedAzp);
log.debug("Logging in");
@ -469,7 +469,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
RealmResource realm = adminClient.realm(bc.providerRealmName());
ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0);
ClientResource clientResource = realm.clients().get(rep.getId());
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "aud", "invalid-aud", ProviderConfigProperty.LIST_TYPE, true, true);
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "aud", "invalid-aud", ProviderConfigProperty.LIST_TYPE, true, true, true);
clientResource.getProtocolMappers().createMapper(hardCodedAzp);
log.debug("Logging in");
@ -755,6 +755,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
}
private static final CustomKcOidcBrokerConfiguration BROKER_CONFIG_INSTANCE = new CustomKcOidcBrokerConfiguration();
static class CustomKcOidcBrokerConfiguration extends KcOidcBrokerConfiguration {
@Override

View file

@ -50,13 +50,11 @@ import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.arquillian.annotation.EnableFeatures;
import org.keycloak.testsuite.util.AdminClientUtil;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.util.BasicAuthHelper;
import com.google.common.collect.ImmutableMap;
@ -77,7 +75,7 @@ public final class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBase
brokerApp.setDirectAccessGrantsEnabled(true);
ClientResource brokerAppResource = providerRealm.clients().get(brokerApp.getId());
brokerAppResource.update(brokerApp);
brokerAppResource.getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true)).close();
brokerAppResource.getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true, true)).close();
IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation();
hardCodedSessionNoteMapper.setName("hard-coded");

View file

@ -47,6 +47,7 @@ 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;
/**
@ -180,7 +181,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);
true, false, true, true);
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep);

View file

@ -105,8 +105,8 @@ public class LDAPMultipleAttributesTest extends AbstractLDAPTest {
ldapClient.addRedirectUri("/ldap-portal");
ldapClient.addRedirectUri("/ldap-portal/*");
ldapClient.setManagementUrl("/ldap-portal");
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("postalCode", "postal_code", "postal_code", "String", true, true, true));
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("street", "street", "street", "String", true, true, false));
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("postalCode", "postal_code", "postal_code", "String", true, true, true, true));
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("street", "street", "street", "String", true, true, true, false));
ldapClient.addScopeMapping(appRealm.getRole("user"));
ldapClient.setSecret("password");
});
@ -237,7 +237,6 @@ public class LDAPMultipleAttributesTest extends AbstractLDAPTest {
}
}

View file

@ -819,7 +819,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
String clientScopeId = ApiUtil.getCreatedId(response);
response.close();
ClientScopeResource clientScopeResource = adminClient.proxy(ClientScopeResource.class, scopeUri);
ProtocolMapperModel hard = HardcodedClaim.create("hard", "hard", "coded", "String", true, true);
ProtocolMapperModel hard = HardcodedClaim.create("hard", "hard", "coded", "String", true, true, true);
ProtocolMapperRepresentation mapper = ModelToRepresentation.toRepresentation(hard);
response = clientScopeResource.getProtocolMappers().createMapper(mapper);
assertEquals(201, response.getStatus());

View file

@ -175,7 +175,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
directPublic.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
directPublic.setFullScopeAllowed(false);
directPublic.addRedirectUri("*");
directPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false));
directPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false, true));
ClientModel directUntrustedPublic = realm.addClient("direct-public-untrusted");
directUntrustedPublic.setClientId("direct-public-untrusted");
@ -186,7 +186,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
directUntrustedPublic.setFullScopeAllowed(false);
directUntrustedPublic.addRedirectUri("*");
directUntrustedPublic.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
directUntrustedPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false));
directUntrustedPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false, true));
ClientModel directNoSecret = realm.addClient("direct-no-secret");
directNoSecret.setClientId("direct-no-secret");

View file

@ -401,7 +401,7 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
String fooScopeId = ApiUtil.getCreatedId(response);
response.close();
ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createAddressMapper(true, true, true);
ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createAddressMapper(true, true, true, true);
response = appRealm.clientScopes().get(fooScopeId).getProtocolMappers().createMapper(protocolMapper);
response.close();

View file

@ -153,11 +153,11 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
reconnectAdminClient();
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, "script-scripts/test-script-mapper1.js", false)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, "script-scripts/test-script-mapper2.js", true)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3","computed-json-via-script", "computed-json-via-script", "JSON", true, true, "script-scripts/test-script-mapper3.js", false)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, true,"script-scripts/test-script-mapper1.js", false)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, true, "script-scripts/test-script-mapper2.js", true)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3","computed-json-via-script", "computed-json-via-script", "JSON", true, true, true, "script-scripts/test-script-mapper3.js", false)).close();
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, "script-scripts/test-bad-script-mapper3.js", false));
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, true, "script-scripts/test-bad-script-mapper3.js", false));
assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR));
response.close();
}
@ -199,31 +199,31 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
ProtocolMapperRepresentation mapper = createAddressMapper(true, true, true);
ProtocolMapperRepresentation mapper = createAddressMapper(true, true, true, true);
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.REGION), "region_some");
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.COUNTRY), "country_some");
mapper.getConfig().remove(AddressMapper.getModelPropertyName(AddressClaimSet.POSTAL_CODE)); // Even if we remove protocolMapper config property, it should still default to postal_code
app.getProtocolMappers().createMapper(mapper).close();
ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", true, true);
ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", true, true, true);
app.getProtocolMappers().createMapper(hard).close();
app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("dotted phone", "phone", "home\\.phone", "String", true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("firstDepartment", "departments", "firstDepartment", "String", true, true, false)).close();
app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("dotted phone", "phone", "home\\.phone", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("firstDepartment", "departments", "firstDepartment", "String", true, true, true,false)).close();
app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded")).close();
app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded")).close();
app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user")).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, "'hello_' + user.username", false)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, "new java.util.ArrayList(['A','B'])", true)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, true, "'hello_' + user.username", false)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, true, "new java.util.ArrayList(['A','B'])", true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper", "json-attribute", "claim-from-json-attribute",
"JSON", true, true, false)).close();
"JSON", true, true, true, false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper-multi", "json-attribute-multi", "claim-from-json-attribute-multi",
"JSON", true, true, true)).close();
"JSON", true, true, true, true)).close();
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, "func_tion foo(){ return 'fail';} foo()", false));
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, true, "func_tion foo(){ return 'fail';} foo()", false));
assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR));
response.close();
}
@ -360,10 +360,10 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create a user attr mapping for some claims that exist as properties in the tokens
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
app.getProtocolMappers().createMapper(createClaimMapper("userid-as-sub", "userid", "sub", "String", true, true, false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("useraud", "useraud", "aud", "String", true, true, true)).close();
app.getProtocolMappers().createMapper(createHardcodedClaim("website-hardcoded", "website", "http://localhost", "String", true, true)).close();
app.getProtocolMappers().createMapper(createHardcodedClaim("iat-hardcoded", "iat", "123", "long", true, false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("userid-as-sub", "userid", "sub", "String", true, true, true,false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("useraud", "useraud", "aud", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createHardcodedClaim("website-hardcoded", "website", "http://localhost", "String", true, true, true)).close();
app.getProtocolMappers().createMapper(createHardcodedClaim("iat-hardcoded", "iat", "123", "long", true, false, true)).close();
// login
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password");
@ -432,6 +432,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
"claim-name",
String.class.getSimpleName(),
true,
true,
true
)))) {
mapperId = getCreatedId(response);
@ -481,6 +482,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
"claim-name",
String.class.getSimpleName(),
true,
true,
true
)))) {
mapperId = getCreatedId(response);
@ -519,8 +521,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.update(user);
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
app.getProtocolMappers().createMapper(createClaimMapper("empty", "empty", "empty", "String", true, true, false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("null", "null", "null", "String", true, true, false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("empty", "empty", "empty", "String", true, true, true,false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("null", "null", "null", "String", true, true, true,false)).close();
}
{
@ -569,8 +571,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
public void testUserRoleToAttributeMappers() throws Exception {
// Add mapper for realm roles
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper("test-app", null, "Client roles mapper", "roles-custom.test-app", true, true);
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper("test-app", null, "Client roles mapper", "roles-custom.test-app", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -792,7 +794,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
public void testRoleMapperWithRoleInheritedFromMoreGroups() throws Exception {
// Create client-mapper
String clientId = "test-app";
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app", true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(clientMapper));
@ -825,8 +827,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
public void testUserGroupRoleToAttributeMappers() throws Exception {
// Add mapper for realm roles
String clientId = "test-app";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, "ta.", "Client roles mapper", "roles-custom.test-app", true, true);
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, "ta.", "Client roles mapper", "roles-custom.test-app", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -861,8 +863,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
@Test
public void testUserGroupRoleToAttributeMappersNotScopedOtherApp() throws Exception {
String clientId = "test-app-authz";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom." + clientId, true, true);
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom." + clientId, true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -901,8 +903,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
@Test
public void testUserGroupRoleToAttributeMappersScoped() throws Exception {
String clientId = "test-app-scope";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app-scope", true, true);
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -935,8 +937,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
@Test
public void testUserGroupRoleToAttributeMappersScopedClientNotSet() throws Exception {
String clientId = "test-app-scope";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true);
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -971,8 +973,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
@Test
public void testSingleValuedRoleMapping() throws Exception {
String clientId = "test-app-scope";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, false);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, false);
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true,false);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, true, false);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -1008,8 +1010,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
final String diffClient = "test-app";
final String realmName = "test";
final ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
final ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(diffClient, null, "Client roles mapper", "roles-custom.test-app", true, true);
final ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
final ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(diffClient, null, "Client roles mapper", "roles-custom.test-app", true, true, true);
try (ClientAttributeUpdater cau = ClientAttributeUpdater.forClient(adminClient, realmName, clientId).setDirectAccessGrantsEnabled(true);
ProtocolMappersUpdater protocolMappers = new ProtocolMappersUpdater(cau.getResource().getProtocolMappers())) {
@ -1057,7 +1059,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.joinGroup(group1.getId());
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true,false, false)).close();
try {
// test it
@ -1097,7 +1099,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.joinGroup(group1.getId());
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
try {
// test it
@ -1138,7 +1140,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.joinGroup(group1.getId());
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, true)).close();
try {
// test it
@ -1177,7 +1179,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.joinGroup(group1.getId());
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
try {
// test it
@ -1212,7 +1214,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
try {
// test it
@ -1248,7 +1250,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, true)).close();
try {
// test it
@ -1291,7 +1293,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
try {
// test it
@ -1336,7 +1338,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
try {
// test it
@ -1383,7 +1385,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, true)).close();
try {
// test it
@ -1434,7 +1436,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
try {
// test it
@ -1483,7 +1485,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
try {
// test it
@ -1532,7 +1534,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
try {
// test it
@ -1586,7 +1588,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true,true, true)).close();
try {
// test it
@ -1776,7 +1778,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
put(ClientScopeModel.DYNAMIC_SCOPE_REGEXP, "dyn-scope-with-mapper:*");
}});
// create the attribute mapper
ProtocolMapperRepresentation protocolMapperRepresentation = createHardcodedClaim("dynamic-scope-hardcoded-mapper", "hardcoded-foo", "hardcoded-bar", "String", true, true);
ProtocolMapperRepresentation protocolMapperRepresentation = createHardcodedClaim("dynamic-scope-hardcoded-mapper", "hardcoded-foo", "hardcoded-bar", "String", true, true, true);
scopeRep.setProtocolMappers(Collections.singletonList(protocolMapperRepresentation));
try (Response resp = adminClient.realm("test").clientScopes().create(scopeRep)) {

View file

@ -119,7 +119,7 @@ public class AudienceTest extends AbstractOIDCScopeTest {
public void testAudienceProtocolMapperWithClientAudience() throws Exception {
// Add audience protocol mapper to the clientScope "audience-scope"
ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper", "service-client",
null, true, false);
null, true, false, true);
ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope");
Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
String mapperId = ApiUtil.getCreatedId(resp);
@ -145,14 +145,14 @@ public class AudienceTest extends AbstractOIDCScopeTest {
public void testAudienceProtocolMapperWithCustomAudience() throws Exception {
// Add audience protocol mapper to the clientScope "audience-scope"
ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 1", null,
"http://host/service/ctx1", true, false);
"http://host/service/ctx1", true, false, true);
ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope");
Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
String mapper1Id = ApiUtil.getCreatedId(resp);
resp.close();
audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 2", null,
"http://host/service/ctx2", true, true);
"http://host/service/ctx2", true, true, true);
resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
String mapper2Id = ApiUtil.getCreatedId(resp);
resp.close();

View file

@ -0,0 +1,472 @@
package org.keycloak.testsuite.oidc;
import jakarta.ws.rs.NotFoundException;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.admin.client.resource.ProtocolMappersResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.Profile;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.AudienceProtocolMapper;
import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
import org.keycloak.protocol.oidc.mappers.HardcodedRole;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.oidc.mappers.RoleNameMapper;
import org.keycloak.protocol.oidc.mappers.SHA256PairwiseSubMapper;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.ProtocolMapperUtil;
import org.keycloak.util.JsonSerialization;
import org.wildfly.common.Assert;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.keycloak.protocol.ProtocolMapperUtils.USER_SESSION_NOTE;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ACR;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ACR_SCOPE;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ADDRESS;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ALLOWED_WEB_ORIGINS;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.AUDIENCE_RESOLVE;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.CLIENT_ROLES;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.EMAIL;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.EMAIL_VERIFIED;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.FAMILY_NAME;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.FULL_NAME;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.GIVEN_NAME;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.PROFILE_CLAIM;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.REALM_ROLES;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ROLES_SCOPE;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.USERNAME;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE;
import static org.keycloak.protocol.oidc.mappers.AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX;
import static org.keycloak.protocol.oidc.mappers.AudienceProtocolMapper.INCLUDED_CLIENT_AUDIENCE;
import static org.keycloak.protocol.oidc.mappers.HardcodedClaim.CLAIM_VALUE;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION;
import static org.keycloak.protocol.oidc.mappers.PairwiseSubMapperHelper.PAIRWISE_SUB_ALGORITHM_SALT;
import static org.keycloak.protocol.oidc.mappers.RoleNameMapper.NEW_ROLE_NAME;
import static org.keycloak.protocol.oidc.mappers.RoleNameMapper.ROLE_CONFIG;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
@EnableFeature(value = Profile.Feature.TOKEN_EXCHANGE, skipRestart = true)
public class LightWeightAccessTokenTest extends AbstractKeycloakTest {
@Before
public void clientConfiguration() {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").directAccessGrant(true).setServiceAccountsEnabled(true);
ClientManager.realm(adminClient.realm("test")).clientId("resource-server").directAccessGrant(true);
}
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
RealmRepresentation realm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
UserRepresentation user = findUser(realm, "test-user@localhost");
Map<String, List<String>> attributes = new HashMap<>(){{
put("street", Arrays.asList("1 My Street"));
put("locality", Arrays.asList("Cardiff"));
put("region", Arrays.asList("Cardiff"));
put("postal_code", Arrays.asList("CF104RA"));
}};
user.setAttributes(attributes);
user.setGroups(Arrays.asList("/topGroup/level2group"));
ClientRepresentation confApp = KeycloakModelUtils.createClient(realm, "resource-server");
confApp.setSecret("password");
confApp.setServiceAccountsEnabled(Boolean.TRUE);
testRealms.add(realm);
}
private UserRepresentation findUser(RealmRepresentation testRealm, String userName) {
for (UserRepresentation user : testRealm.getUsers()) {
if (user.getUsername().equals(userName)) return user;
}
return null;
}
@Test
public void accessTokenFalseIntrospectionTrueTest() throws IOException {
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, false);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
@Test
public void accessTokenTrueIntrospectionFalseTest() throws IOException {
ProtocolMappersResource protocolMappers = setProtocolMappers(true, false, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, true);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
// Most of the claims should not be included in introspectionResponse as introspectionMapper was disabled
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, false, false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
@Test
public void accessTokenTrueIntrospectionTrueTest() throws IOException {
ProtocolMappersResource protocolMappers = setProtocolMappers(true, true, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, true);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
@Test
public void offlineTokenTest() throws IOException {
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true);
try {
oauth.nonce("123456");
oauth.scope("openid address offline_access");
oauth.clientId("test-app");
TokenResponseContext ctx = browserLogin("password", "test-user@localhost", "password");
OAuthClient.AccessTokenResponse response = ctx.tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
System.out.println("idtoken:" + response.getIdToken());
assertAccessToken(oauth.verifyToken(accessToken), true, false);
oauth.clientId("resource-server");
removeSession(ctx.userSessionId);
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
private void removeSession(final String sessionId) {
testingClient.testing().removeExpired("test");
try {
testingClient.testing().removeUserSession("test", sessionId);
} catch (NotFoundException nfe) {
// Ignore
}
}
@Test
public void clientCredentialTest() throws Exception {
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, false);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = oauth.doClientCredentialsGrantAccessTokenRequest("password");
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), false, false);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
@Test
public void exchangeTest() throws Exception {
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, false);
response = oauth.doTokenExchange(TEST, accessToken, null, "test-app", "password");
String exchangedTokenString = response.getAccessToken();
System.out.println("exchangedTokenString:" + exchangedTokenString);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", exchangedTokenString);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, true);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
private void assertMapperClaims(AccessToken token, boolean isAddMapperResponseFlag, boolean isAuthCodeFlow) {
if (isAddMapperResponseFlag) {
if (isAuthCodeFlow) {
Assert.assertNotNull(token.getName());
Assert.assertNotNull(token.getGivenName());
Assert.assertNotNull(token.getFamilyName());
Assert.assertNotNull(token.getAddress());
Assert.assertNotNull(token.getEmail());
Assert.assertNotNull(token.getOtherClaims().get("user-session-note"));
Assert.assertNotNull(token.getOtherClaims().get("test-claim"));
Assert.assertNotNull(token.getOtherClaims().get("group-name"));
}
Assert.assertNotNull(token.getAudience());
Assert.assertNotNull(token.getAcr());
Assert.assertNotNull(token.getAllowedOrigins());
Assert.assertNotNull(token.getRealmAccess());
Assert.assertNotNull(token.getResourceAccess());
Assert.assertNotNull(token.getEmailVerified());
Assert.assertNotNull(token.getPreferredUsername());
} else {
if (isAuthCodeFlow) {
Assert.assertTrue(token.getName() == null);
Assert.assertTrue(token.getGivenName() == null);
Assert.assertTrue(token.getFamilyName() == null);
Assert.assertTrue(token.getAddress() == null);
Assert.assertTrue(token.getEmail() == null);
Assert.assertTrue(token.getOtherClaims().get("user-session-note") == null);
Assert.assertTrue(token.getOtherClaims().get("test-claim") == null);
Assert.assertTrue(token.getOtherClaims().get("group-name") == null);
}
Assert.assertTrue(token.getAcr() == null);
Assert.assertTrue(token.getAllowedOrigins() == null);
Assert.assertTrue(token.getRealmAccess() == null);
Assert.assertTrue(token.getResourceAccess().isEmpty());
Assert.assertTrue(token.getEmailVerified() == null);
Assert.assertTrue(token.getPreferredUsername() == null);
}
}
private void assertInitClaims(AccessToken token, boolean isAuthCodeFlow) {
Assert.assertNotNull(token.getExp());
Assert.assertNotNull(token.getIat());
Assert.assertNotNull(token.getId());
Assert.assertNotNull(token.getType());
if (isAuthCodeFlow) {
Assert.assertNotNull(token.getSessionId());
Assert.assertNotNull(token.getAuth_time());
} else {
Assert.assertTrue(token.getSessionId() == null);
Assert.assertTrue(token.getAuth_time() == null);
}
Assert.assertNotNull(token.getIssuedFor());
Assert.assertNotNull(token.getScope());
Assert.assertNotNull(token.getIssuer());
}
private void assertIntrospectClaims(AccessToken token) {
Assert.assertNotNull(token.getOtherClaims().get("client_id"));
Assert.assertNotNull(token.getOtherClaims().get("active"));
Assert.assertNotNull(token.getOtherClaims().get("token_type"));
}
private void assertNonce(AccessToken token, boolean isAuthCodeFlow, boolean exchangeToken) {
if (isAuthCodeFlow && !exchangeToken) {
Assert.assertNotNull(token.getNonce());
} else {
Assert.assertTrue(token.getNonce() == null);
}
}
private void assertAccessToken(AccessToken token, boolean isAuthCodeFlow, boolean isAddToAccessToken) {
assertNonce(token, isAuthCodeFlow, false);
assertMapperClaims(token, isAddToAccessToken, isAuthCodeFlow);
assertInitClaims(token, isAuthCodeFlow);
}
private void assertTokenIntrospectionResponse(AccessToken token, boolean isAuthCodeFlow) {
assertTokenIntrospectionResponse(token, isAuthCodeFlow, true, false);
}
private void assertTokenIntrospectionResponse(AccessToken token, boolean isAuthCodeFlow, boolean isAddToIntrospect, boolean exchangeToken) {
assertNonce(token, isAuthCodeFlow, exchangeToken);
assertMapperClaims(token, isAddToIntrospect, isAuthCodeFlow);
assertInitClaims(token, isAuthCodeFlow);
assertIntrospectClaims(token);
}
protected RealmResource testRealm() {
return adminClient.realm("test");
}
private void setScopeProtocolMappers(boolean isIncludeAccessToken, boolean isIncludeIntrospection) {
setScopeProtocolMapper(ACR_SCOPE, ACR, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(PROFILE_CLAIM, FULL_NAME, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(EMAIL, EMAIL, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(EMAIL, EMAIL_VERIFIED, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(PROFILE_CLAIM, GIVEN_NAME, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(PROFILE_CLAIM, FAMILY_NAME, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(PROFILE_CLAIM, USERNAME, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(WEB_ORIGINS_SCOPE, ALLOWED_WEB_ORIGINS, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(ROLES_SCOPE, REALM_ROLES, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(ROLES_SCOPE, CLIENT_ROLES, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(ROLES_SCOPE, AUDIENCE_RESOLVE, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(ADDRESS, ADDRESS, isIncludeAccessToken, isIncludeIntrospection);
}
private void setScopeProtocolMapper(String scopeName, String mapperName, boolean isIncludeAccessToken, boolean isIncludeIntrospection) {
ClientScopeResource scope = ApiUtil.findClientScopeByName(testRealm(), scopeName);
ProtocolMapperRepresentation protocolMapper = ApiUtil.findProtocolMapperByName(scope, mapperName);
Map<String, String> config = protocolMapper.getConfig();
if (isIncludeAccessToken) {
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
} else {
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
}
if (isIncludeIntrospection) {
config.put(INCLUDE_IN_INTROSPECTION, "true");
} else {
config.put(INCLUDE_IN_INTROSPECTION, "false");
}
scope.getProtocolMappers().update(protocolMapper.getId(), protocolMapper);
}
private ProtocolMappersResource setProtocolMappers(boolean isIncludeAccessToken, boolean isIncludeIntrospection, boolean setPairWise) {
setScopeProtocolMappers(isIncludeAccessToken, isIncludeIntrospection);
List<ProtocolMapperRepresentation> protocolMapperList = new ArrayList<>();
setExistingProtocolMappers(protocolMapperList, isIncludeAccessToken, isIncludeIntrospection, setPairWise);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(protocolMapperList);
return protocolMappers;
}
private void setExistingProtocolMappers(List<ProtocolMapperRepresentation> protocolMapperList, boolean isIncludeAccessToken, boolean isIncludeIntrospection, boolean setPairWise) {
Map<String, String> config = new HashMap<>();
if (isIncludeAccessToken) {
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
} else {
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
}
if (isIncludeIntrospection) {
config.put(INCLUDE_IN_INTROSPECTION, "true");
} else {
config.put(INCLUDE_IN_INTROSPECTION, "false");
}
ProtocolMapperRepresentation audienceProtocolMapper = createClaimMapper("audience", AudienceProtocolMapper.PROVIDER_ID, new HashMap<>(config) {{
put(INCLUDED_CLIENT_AUDIENCE, "account-console");
}});
protocolMapperList.add(audienceProtocolMapper);
ProtocolMapperRepresentation roleNameMapper = createClaimMapper("role-name", RoleNameMapper.PROVIDER_ID, new HashMap<>(config) {{
put(ROLE_CONFIG, "user");
put(NEW_ROLE_NAME, "new-role");
}});
protocolMapperList.add(roleNameMapper);
ProtocolMapperRepresentation groupMembershipMapper = createClaimMapper("group-member", GroupMembershipMapper.PROVIDER_ID, new HashMap<>(config) {{
put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "group-name");
}});
protocolMapperList.add(groupMembershipMapper);
ProtocolMapperRepresentation hardcodedClaim = createClaimMapper("hardcoded-claim", HardcodedClaim.PROVIDER_ID, new HashMap<>(config) {{
put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "test-claim");
put(CLAIM_VALUE, "test-value");
}});
protocolMapperList.add(hardcodedClaim);
ProtocolMapperRepresentation hardcodedRole = createClaimMapper("hardcoded-role", HardcodedRole.PROVIDER_ID, new HashMap<>(config) {{
put(ROLE_CONFIG, "hardcoded-role");
}});
protocolMapperList.add(hardcodedRole);
ProtocolMapperRepresentation userSessionNoteMapper = createClaimMapper("user-session-note", UserSessionNoteMapper.PROVIDER_ID, new HashMap<>(config) {{
put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "user-session-note");
put(USER_SESSION_NOTE, "AUTH_TIME");
}});
protocolMapperList.add(userSessionNoteMapper);
if (setPairWise) {
ProtocolMapperRepresentation pairwiseSubMapper = createClaimMapper("pairwise-sub-mapper", "oidc-" + SHA256PairwiseSubMapper.PROVIDER_ID + PROVIDER_ID_SUFFIX, new HashMap<>(config) {{
put(PAIRWISE_SUB_ALGORITHM_SALT, "abc");
}});
protocolMapperList.add(pairwiseSubMapper);
}
}
private static ProtocolMapperRepresentation createClaimMapper(String name, String providerId, Map<String, String> config) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(providerId);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
mapper.setConfig(config);
return ModelToRepresentation.toRepresentation(mapper);
}
private void deleteProtocolMappers(ProtocolMappersResource protocolMappers) {
List<String> mapperNames = new ArrayList<>(Arrays.asList("reference", "audience", "role-name", "group-member", "hardcoded-claim", "hardcoded-role", "user-session-note", "pairwise-sub-mapper"));
List<ProtocolMapperRepresentation> mappers = new ArrayList<>();
for (String mapperName : mapperNames) {
mappers.add(ProtocolMapperUtil.getMapperByNameAndProtocol(protocolMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, mapperName));
}
for (ProtocolMapperRepresentation mapper : mappers) {
if (mapper != null) {
protocolMappers.delete(mapper.getId());
}
}
}
private TokenResponseContext browserLogin(String clientSecret, String username, String password) {
OAuthClient.AuthorizationEndpointResponse authsEndpointResponse = oauth.doLogin(username, password);
String userSessionId = authsEndpointResponse.getSessionState();
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), clientSecret);
return new TokenResponseContext(userSessionId, tokenResponse);
}
private class TokenResponseContext {
private final String userSessionId;
private final OAuthClient.AccessTokenResponse tokenResponse;
public TokenResponseContext(String userSessionId, OAuthClient.AccessTokenResponse tokenResponse) {
this.userSessionId = userSessionId;
this.tokenResponse = tokenResponse;
}
}
}

View file

@ -92,7 +92,7 @@ public class DeployedScriptMapperTest extends AbstractTestRealmKeycloakTest {
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script",
"computed-via-script", "String", true, true, "'hello_' + user.username", false);
"computed-via-script", "String", true, true, true, "'hello_' + user.username", false);
mapper.setProtocolMapper("script-mapper-a.js");

View file

@ -95,7 +95,7 @@ public class UndeployedScriptMapperNotAvailableTest extends AbstractTestRealmKey
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
{
ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script",
"computed-via-script", "String", true, true, "'hello_' + user.username", false);
"computed-via-script", "String", true, true, true, "'hello_' + user.username", false);
mapper.setProtocolMapper("script-mapper-a.js");

View file

@ -47,8 +47,8 @@ public class ProtocolMapperUtil {
* @param accessToken
* @return
*/
public static ProtocolMapperRepresentation createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo) {
return ModelToRepresentation.toRepresentation(AddressMapper.createAddressMapper(idToken, accessToken, userInfo));
public static ProtocolMapperRepresentation createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo, boolean introspectionEndpoint) {
return ModelToRepresentation.toRepresentation(AddressMapper.createAddressMapper(idToken, accessToken, userInfo, introspectionEndpoint));
}
/**
@ -65,9 +65,9 @@ public class ProtocolMapperUtil {
public static ProtocolMapperRepresentation createHardcodedClaim(String name,
String hardcodedName,
String hardcodedValue, String claimType,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return ModelToRepresentation.toRepresentation(HardcodedClaim.create(name, hardcodedName, hardcodedValue,
claimType, accessToken, idToken));
claimType, accessToken, idToken, introspectionEndpoint));
}
/**
@ -85,64 +85,64 @@ public class ProtocolMapperUtil {
public static ProtocolMapperRepresentation createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, boolean multivalued) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multivalued) {
return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName,
claimType, accessToken, idToken, multivalued, false));
claimType, accessToken, idToken, introspectionEndpoint, multivalued, false));
}
public static ProtocolMapperRepresentation createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
boolean accessToken, boolean idToken,
boolean accessToken, boolean idToken, boolean introspectionEndpoint,
boolean multivalued, boolean aggregateAttrs) {
return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName,
claimType, accessToken, idToken, multivalued, aggregateAttrs));
claimType, accessToken, idToken, introspectionEndpoint, multivalued, aggregateAttrs));
}
public static ProtocolMapperRepresentation createClaimMapper(String name,
String userSessionNote,
String tokenClaimName, String jsonType,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return ModelToRepresentation.toRepresentation(UserSessionNoteMapper.createClaimMapper(name,
userSessionNote,
tokenClaimName, jsonType,
accessToken, idToken));
accessToken, idToken, introspectionEndpoint));
}
public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return createUserRealmRoleMappingMapper(realmRolePrefix, name, tokenClaimName, accessToken, idToken, true);
return createUserRealmRoleMappingMapper(realmRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, true);
}
public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, multiValued));
return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, multiValued));
}
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return createUserClientRoleMappingMapper(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, true);
return createUserClientRoleMappingMapper(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, true);
}
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
String name,
String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, multiValued));
return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, multiValued));
}
public static ProtocolMapperRepresentation getMapperByNameAndProtocol(ProtocolMappersResource protocolMappers, String protocol, String name) {
@ -160,11 +160,12 @@ public class ProtocolMapperUtil {
String claimType,
boolean accessToken,
boolean idToken,
boolean introspectionEndpoint,
String script,
boolean multiValued) {
return ModelToRepresentation.toRepresentation(
ScriptBasedOIDCProtocolMapper.create(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, script, multiValued)
ScriptBasedOIDCProtocolMapper.create(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, introspectionEndpoint, script, multiValued)
);
}
@ -175,10 +176,10 @@ public class ProtocolMapperUtil {
public static ProtocolMapperRepresentation createAudienceMapper(String name,
String includedClientAudience,
String includedCustomAudience,
boolean accessToken, boolean idToken) {
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return ModelToRepresentation.toRepresentation(
AudienceProtocolMapper.createClaimMapper(name, includedClientAudience, includedCustomAudience, accessToken, idToken)
AudienceProtocolMapper.createClaimMapper(name, includedClientAudience, includedCustomAudience, accessToken, idToken, introspectionEndpoint)
);
}
}