From 753feae49ed4f99d87b5b5ebfadc3b7de2f18373 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Mon, 2 Mar 2015 21:39:43 -0500 Subject: [PATCH] fixes --- .../idm/ProtocolMapperTypeRepresentation.java | 18 + .../models/utils/RepresentationToModel.java | 14 - .../org/keycloak/protocol/ProtocolMapper.java | 21 ++ .../oidc/OIDCLoginProtocolFactory.java | 15 +- .../keycloak/protocol/oidc/TokenManager.java | 4 +- .../protocol/oidc/UserInfoService.java | 308 +++++++++--------- .../oidc/mappers/OIDCAccessTokenMapper.java | 4 +- .../oidc/mappers/OIDCAddressMapper.java | 37 ++- .../mappers/OIDCAttributeMapperHelper.java | 20 +- .../oidc/mappers/OIDCFullNameMapper.java | 35 +- .../oidc/mappers/OIDCIDTokenMapper.java | 18 + .../oidc/mappers/OIDCUserAttributeMapper.java | 51 ++- .../oidc/mappers/OIDCUserModelMapper.java | 61 +++- .../admin/ServerInfoAdminResource.java | 2 + .../testsuite/admin/AdminAPITest.java | 4 - 15 files changed, 406 insertions(+), 206 deletions(-) create mode 100755 services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCIDTokenMapper.java diff --git a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java index 78e13bda9a..1e510f8bfa 100755 --- a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java @@ -16,6 +16,8 @@ public class ProtocolMapperTypeRepresentation { protected String name; protected String label; protected String helpText; + protected String type; + protected String defaultValue; public String getName() { return name; @@ -33,6 +35,22 @@ public class ProtocolMapperTypeRepresentation { this.label = label; } + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + public String getHelpText() { return helpText; } diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index 2bf61e2395..f24a094c24 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -457,12 +457,6 @@ public class RepresentationToModel { applicationModel.updateDefaultRoles(resourceRep.getDefaultRoles()); } - if (resourceRep.getClaims() != null) { - setClaims(applicationModel, resourceRep.getClaims()); - } else { - applicationModel.setAllowedClaimsMask(ClaimMask.ALL); - } - if (resourceRep.getProtocolMappers() != null) { Set ids = new HashSet(); for (ClientProtocolMappingRepresentation map : resourceRep.getProtocolMappers()) { @@ -524,10 +518,6 @@ public class RepresentationToModel { } } - if (rep.getClaims() != null) { - setClaims(resource, rep.getClaims()); - } - updateClientIdentityProvides(rep.getIdentityProviders(), resource); } @@ -633,10 +623,6 @@ public class RepresentationToModel { model.setWebOrigins(new HashSet(webOrigins)); } - if (rep.getClaims() != null) { - setClaims(model, rep.getClaims()); - } - if (rep.getNotBefore() != null) { model.setNotBefore(rep.getNotBefore()); } diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java index 2c3d230d29..ff6e10591c 100755 --- a/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java +++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java @@ -16,9 +16,14 @@ public interface ProtocolMapper extends Provider, ProviderFactory claims = new HashMap(); - claims.putAll(userInfo.getOtherClaims()); - claims.put("sub", userModel.getId()); - return Cors.add(request, Response.ok(claims)).auth().allowedOrigins(accessToken).build(); - } catch (Exception e) { - throw new UnauthorizedException("Could not retrieve user info.", e); - } - } - -} +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.protocol.oidc; + +import org.jboss.resteasy.annotations.cache.NoCache; +import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; +import org.jboss.resteasy.spi.UnauthorizedException; +import org.keycloak.ClientConnection; +import org.keycloak.events.Details; +import org.keycloak.events.EventBuilder; +import org.keycloak.events.EventType; +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.representations.AccessToken; +import org.keycloak.representations.UserClaimSet; +import org.keycloak.services.managers.AppAuthManager; +import org.keycloak.services.managers.EventsManager; +import org.keycloak.services.resources.Cors; + +import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import java.util.HashMap; +import java.util.Map; + +/** + * @author pedroigor + */ +public class UserInfoService { + + @Context + private HttpRequest request; + + @Context + private HttpResponse response; + + @Context + private KeycloakSession session; + + @Context + private ClientConnection clientConnection; + + private final TokenManager tokenManager; + private final AppAuthManager appAuthManager; + private final OIDCLoginProtocolService openIdConnectService; + private final RealmModel realmModel; + + public UserInfoService(OIDCLoginProtocolService openIDConnectService) { + this.realmModel = openIDConnectService.getRealm(); + + if (this.realmModel == null) { + throw new RuntimeException("Null realm."); + } + + this.tokenManager = openIDConnectService.getTokenManager(); + + if (this.tokenManager == null) { + throw new RuntimeException("Null token manager."); + } + + this.openIdConnectService = openIDConnectService; + this.appAuthManager = new AppAuthManager(); + } + + @Path("/") + @OPTIONS + @Produces(MediaType.APPLICATION_JSON) + public Response issueUserInfoPreflight() { + return Cors.add(this.request, Response.ok()).auth().preflight().build(); + } + + @Path("/") + @GET + @NoCache + @Produces(MediaType.APPLICATION_JSON) + public Response issueUserInfoGet(@Context final HttpHeaders headers) { + String accessToken = this.appAuthManager.extractAuthorizationHeaderToken(headers); + return issueUserInfo(accessToken); + } + + @Path("/") + @POST + @NoCache + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_JSON) + public Response issueUserInfoPost(@FormParam("access_token") String accessToken) { + return issueUserInfo(accessToken); + } + + private Response issueUserInfo(String token) { + try { + EventBuilder event = new EventsManager(this.realmModel, this.session, this.clientConnection).createEventBuilder() + .event(EventType.USER_INFO_REQUEST) + .detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN); + + Response validationResponse = this.openIdConnectService.validateAccessToken(token); + + if (!AccessToken.class.isInstance(validationResponse.getEntity())) { + event.error(EventType.USER_INFO_REQUEST.name()); + return Response.fromResponse(validationResponse).status(Status.FORBIDDEN).build(); + } + + AccessToken accessToken = (AccessToken) validationResponse.getEntity(); + UserSessionModel userSession = session.sessions().getUserSession(realmModel, accessToken.getSessionState()); + ClientModel clientModel = realmModel.findClient(accessToken.getIssuedFor()); + UserModel userModel = userSession.getUser(); + AccessToken userInfo = new AccessToken(); + this.tokenManager.transformToken(session, userInfo, realmModel, clientModel, userModel, userSession, null); + + event + .detail(Details.USERNAME, userModel.getUsername()) + .client(clientModel) + .session(userSession) + .user(userModel) + .success(); + + Map claims = new HashMap(); + claims.putAll(userInfo.getOtherClaims()); + claims.put("sub", userModel.getId()); + return Cors.add(request, Response.ok(claims)).auth().allowedOrigins(accessToken).build(); + } catch (Exception e) { + throw new UnauthorizedException("Could not retrieve user info.", e); + } + } + +} diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAccessTokenMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAccessTokenMapper.java index e2c479fb4a..eebda6ff6a 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAccessTokenMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAccessTokenMapper.java @@ -12,6 +12,6 @@ import org.keycloak.representations.AccessToken; */ public interface OIDCAccessTokenMapper { - AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession); + AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession); } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddressMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddressMapper.java index 9b3adab21e..da7a25893a 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddressMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddressMapper.java @@ -6,6 +6,7 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; import org.keycloak.representations.UserClaimSet; import java.util.ArrayList; @@ -17,12 +18,26 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCAddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { +public class OIDCAddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); static { - + ConfigProperty property; + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN); + property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL); + property.setType(ConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT); + configProperties.add(property); + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN); + property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL); + property.setType(ConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); + configProperties.add(property); } public static final String PROVIDER_ID = "oidc-address-mapper"; @@ -53,8 +68,21 @@ public class OIDCAddressMapper extends AbstractOIDCProtocolMapper implements OID } @Override - public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { + public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession) { + if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; + setClaim(token, userSession); + return token; + } + + @Override + public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { + if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; + setClaim(token, userSession); + return token; + } + + protected void setClaim(IDToken token, UserSessionModel userSession) { UserModel user = userSession.getUser(); UserClaimSet.AddressClaimSet addressSet = new UserClaimSet.AddressClaimSet(); addressSet.setStreetAddress(user.getAttribute("street")); @@ -63,7 +91,6 @@ public class OIDCAddressMapper extends AbstractOIDCProtocolMapper implements OID addressSet.setPostalCode(user.getAttribute("postal_code")); addressSet.setCountry(user.getAttribute("country")); token.getOtherClaims().put("address", addressSet); - return token; } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java index a6c98bebcd..552e4718d0 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java @@ -5,6 +5,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; import java.util.HashMap; import java.util.Map; @@ -16,6 +17,12 @@ import java.util.Map; public class OIDCAttributeMapperHelper { public static final String TOKEN_CLAIM_NAME = "Token Claim Name"; public static final String JSON_TYPE = "Claim JSON Type"; + public static final String INCLUDE_IN_ACCESS_TOKEN = "access.token.claim"; + public static final String INCLUDE_IN_ACCESS_TOKEN_LABEL = "Add to access token"; + public static final String INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT = "Should the claim be added to the access token?"; + public static final String INCLUDE_IN_ID_TOKEN = "id.token.claim"; + public static final String INCLUDE_IN_ID_TOKEN_LABEL = "Add to ID token"; + public static final String INCLUDE_IN_ID_TOKEN_HELP_TEXT = "Should the claim be added to the ID token?"; public static Object mapAttributeValue(ProtocolMapperModel mappingModel, Object attributeValue) { if (attributeValue == null) return null; @@ -40,7 +47,7 @@ public class OIDCAttributeMapperHelper { return attributeValue; } - public static void mapClaim(AccessToken token, ProtocolMapperModel mappingModel, Object attributeValue) { + public static void mapClaim(IDToken token, ProtocolMapperModel mappingModel, Object attributeValue) { if (attributeValue == null) return; attributeValue = mapAttributeValue(mappingModel, attributeValue); String protocolClaim = mappingModel.getConfig().get(TOKEN_CLAIM_NAME); @@ -65,6 +72,7 @@ public class OIDCAttributeMapperHelper { String tokenClaimName, String claimType, boolean consentRequired, String consentText, boolean appliedByDefault, + boolean accessToken, boolean idToken, String mapperId) { ProtocolMapperModel mapper = realm.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, name); if (mapper != null) return; @@ -79,7 +87,17 @@ public class OIDCAttributeMapperHelper { config.put(ProtocolMapperUtils.USER_ATTRIBUTE, userAttribute); config.put(TOKEN_CLAIM_NAME, tokenClaimName); config.put(JSON_TYPE, claimType); + if (accessToken) config.put(INCLUDE_IN_ACCESS_TOKEN, "true"); + if (idToken) config.put(INCLUDE_IN_ID_TOKEN, "true"); mapper.setConfig(config); realm.addProtocolMapper(mapper); } + + public static boolean includeInIDToken(ProtocolMapperModel mappingModel) { + return "true".equals(mappingModel.getConfig().get(INCLUDE_IN_ID_TOKEN)); + } + + public static boolean includeInAccessToken(ProtocolMapperModel mappingModel) { + return "true".equals(mappingModel.getConfig().get(INCLUDE_IN_ACCESS_TOKEN)); + } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCFullNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCFullNameMapper.java index 169494a514..f91abe5515 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCFullNameMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCFullNameMapper.java @@ -6,6 +6,7 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; import java.util.ArrayList; import java.util.List; @@ -16,11 +17,26 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCFullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { +public class OIDCFullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); static { + ConfigProperty property; + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN); + property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL); + property.setType(ConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT); + configProperties.add(property); + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN); + property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL); + property.setType(ConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); + configProperties.add(property); } @@ -52,13 +68,24 @@ public class OIDCFullNameMapper extends AbstractOIDCProtocolMapper implements OI } @Override - public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { + public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession) { + if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; + setClaim(token, userSession); + return token; + } + + protected void setClaim(IDToken token, UserSessionModel userSession) { UserModel user = userSession.getUser(); String first = user.getFirstName() == null ? "" : user.getFirstName() + " "; String last = user.getLastName() == null ? "" : user.getLastName(); token.getOtherClaims().put("name", first + last); - return token; } + @Override + public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { + if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; + setClaim(token, userSession); + return token; + } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCIDTokenMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCIDTokenMapper.java new file mode 100755 index 0000000000..932b431e49 --- /dev/null +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCIDTokenMapper.java @@ -0,0 +1,18 @@ +package org.keycloak.protocol.oidc.mappers; + +import org.keycloak.models.ClientSessionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface OIDCIDTokenMapper { + + IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession); +} diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java index 3586372f77..98045ba72f 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java @@ -8,6 +8,7 @@ import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; import java.util.ArrayList; import java.util.List; @@ -20,7 +21,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { +public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); @@ -30,12 +31,35 @@ public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implemen property.setName(ProtocolMapperUtils.USER_ATTRIBUTE); property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL); property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT); + property.setType(ConfigProperty.STRING_TYPE); configProperties.add(property); property = new ConfigProperty(); property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME); property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME); + property.setType(ConfigProperty.STRING_TYPE); property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created."); configProperties.add(property); + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.JSON_TYPE); + property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE); + property.setType(ConfigProperty.STRING_TYPE); + property.setDefaultValue(ConfigProperty.STRING_TYPE); + property.setHelpText("JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values."); + configProperties.add(property); + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN); + property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL); + property.setType(ConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT); + configProperties.add(property); + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN); + property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL); + property.setType(ConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); + configProperties.add(property); } @@ -67,13 +91,26 @@ public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implemen } @Override - public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { + public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession) { + if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; + + setClaim(token, mappingModel, userSession); + return token; + } + + protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { UserModel user = userSession.getUser(); String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); String attributeValue = user.getAttribute(attributeName); - if (attributeValue == null) return token; + if (attributeValue == null) return; OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attributeValue); + } + + @Override + public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { + if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; + setClaim(token, mappingModel, userSession); return token; } @@ -81,11 +118,13 @@ public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implemen String userAttribute, String tokenClaimName, String claimType, boolean consentRequired, String consentText, - boolean appliedByDefault) { + boolean appliedByDefault, + boolean accessToken, boolean idToken) { OIDCAttributeMapperHelper.addClaimMapper(realm, name, userAttribute, tokenClaimName, claimType, consentRequired, consentText, - appliedByDefault, PROVIDER_ID); + appliedByDefault, accessToken, idToken, + PROVIDER_ID); } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java index 5e97f6d1d4..5efa6c6acd 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java @@ -8,6 +8,7 @@ import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; import java.util.ArrayList; import java.util.List; @@ -20,7 +21,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { +public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); static { @@ -28,14 +29,43 @@ public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements O property = new ConfigProperty(); property.setName(ProtocolMapperUtils.USER_ATTRIBUTE); property.setLabel(ProtocolMapperUtils.USER_MODEL_PROPERTY_LABEL); + property.setType(ConfigProperty.STRING_TYPE); property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT); configProperties.add(property); property = new ConfigProperty(); property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME); property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME); + property.setType(ConfigProperty.STRING_TYPE); property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created."); configProperties.add(property); - + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.JSON_TYPE); + property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE); + property.setType(ConfigProperty.STRING_TYPE); + property.setDefaultValue(ConfigProperty.STRING_TYPE); + property.setHelpText("JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values."); + configProperties.add(property); + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.JSON_TYPE); + property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE); + property.setType(ConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText("JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values."); + configProperties.add(property); + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN); + property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL); + property.setType(ConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT); + configProperties.add(property); + property = new ConfigProperty(); + property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN); + property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL); + property.setType(ConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); + configProperties.add(property); } public static final String PROVIDER_ID = "oidc-usermodel-property-mapper"; @@ -66,25 +96,40 @@ public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements O } @Override - public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { + public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession) { + if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; + setClaim(token, mappingModel, userSession); + + return token; + } + + @Override + public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { + if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; + setClaim(token, mappingModel, userSession); + + return token; + } + + protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { UserModel user = userSession.getUser(); String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName); OIDCAttributeMapperHelper.mapClaim(token, mappingModel, propertyValue); - - return token; } public static void addClaimMapper(RealmModel realm, String name, String userAttribute, String tokenClaimName, String claimType, boolean consentRequired, String consentText, - boolean appliedByDefault) { + boolean appliedByDefault, + boolean accessToken, boolean idToken) { OIDCAttributeMapperHelper.addClaimMapper(realm, name, userAttribute, tokenClaimName, claimType, consentRequired, consentText, - appliedByDefault, PROVIDER_ID); + appliedByDefault, accessToken, idToken, + PROVIDER_ID); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java index 6871e95192..0c870934c8 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java @@ -150,6 +150,8 @@ public class ServerInfoAdminResource { ProtocolMapperTypeRepresentation.ConfigProperty propRep = new ProtocolMapperTypeRepresentation.ConfigProperty(); propRep.setName(prop.getName()); propRep.setLabel(prop.getLabel()); + propRep.setType(prop.getType()); + propRep.setDefaultValue(prop.getDefaultValue()); propRep.setHelpText(prop.getHelpText()); rep.getProperties().add(propRep); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java index 68e3f13540..100f9c1f90 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java @@ -214,10 +214,6 @@ public class AdminAPITest { Assert.assertEquals(set, storedSet); } - - if (appRep.getClaims() != null) { - Assert.assertEquals(appRep.getClaims(), storedApp.getClaims()); - } } protected void checkRealmRep(RealmRepresentation rep, RealmRepresentation storedRealm) {