diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java
index e2b55e139e..a21aa65c7b 100755
--- a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java
+++ b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java
@@ -30,6 +30,7 @@ import org.keycloak.migration.migrators.MigrateTo1_9_2;
import org.keycloak.migration.migrators.MigrateTo2_0_0;
import org.keycloak.migration.migrators.MigrateTo2_1_0;
import org.keycloak.migration.migrators.MigrateTo2_2_0;
+import org.keycloak.migration.migrators.MigrateTo2_3_0;
import org.keycloak.migration.migrators.Migration;
import org.keycloak.models.KeycloakSession;
@@ -53,6 +54,7 @@ public class MigrationModelManager {
new MigrateTo2_0_0(),
new MigrateTo2_1_0(),
new MigrateTo2_2_0(),
+ new MigrateTo2_3_0(),
};
public static void migrate(KeycloakSession session) {
diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java
new file mode 100644
index 0000000000..80862ca4f2
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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.migration.migrators;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.keycloak.migration.ModelVersion;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperContainerModel;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RealmModel;
+
+/**
+ * @author Marek Posolda
+ */
+public class MigrateTo2_3_0 implements Migration {
+
+ public static final ModelVersion VERSION = new ModelVersion("2.3.0");
+
+ @Override
+ public void migrate(KeycloakSession session) {
+ for (RealmModel realm : session.realms().getRealms()) {
+ for (ClientModel client : realm.getClients()) {
+ updateProtocolMappers(client);
+ }
+
+ for (ClientTemplateModel clientTemplate : realm.getClientTemplates()) {
+ updateProtocolMappers(clientTemplate);
+ }
+ }
+ }
+
+ private void updateProtocolMappers(ProtocolMapperContainerModel client) {
+ List toUpdate = new LinkedList<>();
+ for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
+ if (!mapper.getConfig().containsKey("userinfo.token.claim") && mapper.getConfig().containsKey("id.token.claim")) {
+ mapper.getConfig().put("userinfo.token.claim", mapper.getConfig().get("id.token.claim"));
+ toUpdate.add(mapper);
+ }
+ }
+
+ for (ProtocolMapperModel mapper : toUpdate) {
+ client.updateProtocolMapper(mapper);
+ }
+ }
+
+ @Override
+ public ModelVersion getVersion() {
+ return VERSION;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractUserRoleMappingMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractUserRoleMappingMapper.java
index 37434cf57d..de4d0548b3 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractUserRoleMappingMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractUserRoleMappingMapper.java
@@ -35,7 +35,7 @@ import java.util.Set;
*
* @author Thomas Darimont
*/
-abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper {
+abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
/**
* Returns the role names extracted from the given {@code roleModels} while recursively traversing "Composite Roles".
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
index f7d30a7871..674c9ff10c 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
@@ -44,21 +44,7 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
private static final List configProperties = new ArrayList();
static {
- ProviderConfigProperty property;
- property = new ProviderConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
- configProperties.add(property);
- property = new ProviderConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
- configProperties.add(property);
+ OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AddressMapper.class);
}
public static final String PROVIDER_ID = "oidc-address-mapper";
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java
index 1675f60f36..1e4ad9df09 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java
@@ -43,21 +43,7 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
private static final List configProperties = new ArrayList();
static {
- ProviderConfigProperty property;
- property = new ProviderConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
- configProperties.add(property);
- property = new ProviderConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
- configProperties.add(property);
+ OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, FullNameMapper.class);
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java
index ee179fb158..41dbb47db9 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java
@@ -45,16 +45,8 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
private static final List configProperties = new ArrayList();
static {
- ProviderConfigProperty property;
- ProviderConfigProperty property1;
- property1 = new ProviderConfigProperty();
- property1.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
- property1.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
- property1.setType(ProviderConfigProperty.STRING_TYPE);
- property1.setDefaultValue("groups");
- property1.setHelpText(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_TOOLTIP);
- configProperties.add(property1);
- property1 = new ProviderConfigProperty();
+ OIDCAttributeMapperHelper.addTokenClaimNameConfig(configProperties);
+ ProviderConfigProperty property1 = new ProviderConfigProperty();
property1.setName("full.path");
property1.setLabel("Full group path");
property1.setType(ProviderConfigProperty.BOOLEAN_TYPE);
@@ -62,23 +54,7 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
property1.setHelpText("Include full path to group i.e. /top/level1/level2, false will just specify the group name");
configProperties.add(property1);
- property1 = new ProviderConfigProperty();
- property1.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
- property1.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
- property1.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property1.setDefaultValue("true");
- property1.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
- configProperties.add(property1);
- property1 = new ProviderConfigProperty();
- property1.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
- property1.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
- property1.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property1.setDefaultValue("true");
- property1.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
- configProperties.add(property1);
-
-
-
+ OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, GroupMembershipMapper.class);
}
public static final String PROVIDER_ID = "oidc-group-membership-mapper";
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
index 571a6a22ab..40628245dd 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
@@ -44,46 +44,17 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
public static final String CLAIM_VALUE = "claim.value";
static {
- ProviderConfigProperty property;
- property = new ProviderConfigProperty();
- property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
- property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
- property.setType(ProviderConfigProperty.STRING_TYPE);
- property.setHelpText("Claim name you want to hard code 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 ProviderConfigProperty();
+ OIDCAttributeMapperHelper.addTokenClaimNameConfig(configProperties);
+
+ ProviderConfigProperty property = new ProviderConfigProperty();
property.setName(CLAIM_VALUE);
property.setLabel("Claim value");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setHelpText("Value of the claim you want to hard code. 'true' and 'false can be used for boolean values.");
configProperties.add(property);
- property = new ProviderConfigProperty();
- property.setName(OIDCAttributeMapperHelper.JSON_TYPE);
- property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE);
- List types = new ArrayList(3);
- types.add("String");
- types.add("long");
- types.add("int");
- types.add("boolean");
- property.setType(ProviderConfigProperty.LIST_TYPE);
- property.setOptions(types);
- property.setHelpText("JSON type that should be used for the value of the claim. long, int, boolean, and String are valid values.");
- configProperties.add(property);
- property = new ProviderConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
- configProperties.add(property);
- property = new ProviderConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
- configProperties.add(property);
+ OIDCAttributeMapperHelper.addJsonTypeConfig(configProperties);
+ OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, HardcodedClaim.class);
}
public static final String PROVIDER_ID = "oidc-hardcoded-claim-mapper";
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 af829217b8..99b261074b 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
@@ -18,6 +18,7 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
@@ -162,20 +163,34 @@ public class OIDCAttributeMapperHelper {
}
public static boolean includeInUserInfo(ProtocolMapperModel mappingModel){
- return "true".equals(mappingModel.getConfig().get(INCLUDE_IN_USERINFO));
+ String includeInUserInfo = mappingModel.getConfig().get(INCLUDE_IN_USERINFO);
+
+ // Backwards compatibility
+ if (includeInUserInfo == null && includeInIDToken(mappingModel)) {
+ return true;
+ }
+
+ return "true".equals(includeInUserInfo);
}
- public static void addAttributeConfig(List configProperties) {
+ public static void addAttributeConfig(List configProperties, Class extends ProtocolMapper> protocolMapperClass) {
+ addTokenClaimNameConfig(configProperties);
+ addJsonTypeConfig(configProperties);
- ProviderConfigProperty property;
- property = new ProviderConfigProperty();
+ addIncludeInTokensConfig(configProperties, protocolMapperClass);
+ }
+
+ public static void addTokenClaimNameConfig(List configProperties) {
+ ProviderConfigProperty property = new ProviderConfigProperty();
property.setName(TOKEN_CLAIM_NAME);
property.setLabel(TOKEN_CLAIM_NAME_LABEL);
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setHelpText(TOKEN_CLAIM_NAME_TOOLTIP);
configProperties.add(property);
+ }
- property = new ProviderConfigProperty();
+ public static void addJsonTypeConfig(List configProperties) {
+ ProviderConfigProperty property = new ProviderConfigProperty();
property.setName(JSON_TYPE);
property.setLabel(JSON_TYPE);
List types = new ArrayList(3);
@@ -187,29 +202,37 @@ public class OIDCAttributeMapperHelper {
property.setOptions(types);
property.setHelpText(JSON_TYPE_TOOLTIP);
configProperties.add(property);
+ }
- property = new ProviderConfigProperty();
- property.setName(INCLUDE_IN_ID_TOKEN);
- property.setLabel(INCLUDE_IN_ID_TOKEN_LABEL);
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(INCLUDE_IN_ID_TOKEN_HELP_TEXT);
- configProperties.add(property);
+ public static void addIncludeInTokensConfig(List configProperties, Class extends ProtocolMapper> protocolMapperClass) {
+ if (OIDCIDTokenMapper.class.isAssignableFrom(protocolMapperClass)) {
+ ProviderConfigProperty property = new ProviderConfigProperty();
+ property.setName(INCLUDE_IN_ID_TOKEN);
+ property.setLabel(INCLUDE_IN_ID_TOKEN_LABEL);
+ property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
+ property.setDefaultValue("true");
+ property.setHelpText(INCLUDE_IN_ID_TOKEN_HELP_TEXT);
+ configProperties.add(property);
+ }
- property = new ProviderConfigProperty();
- property.setName(INCLUDE_IN_ACCESS_TOKEN);
- property.setLabel(INCLUDE_IN_ACCESS_TOKEN_LABEL);
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
- configProperties.add(property);
+ if (OIDCAccessTokenMapper.class.isAssignableFrom(protocolMapperClass)) {
+ ProviderConfigProperty property = new ProviderConfigProperty();
+ property.setName(INCLUDE_IN_ACCESS_TOKEN);
+ property.setLabel(INCLUDE_IN_ACCESS_TOKEN_LABEL);
+ property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
+ property.setDefaultValue("true");
+ property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
+ configProperties.add(property);
+ }
- property = new ProviderConfigProperty();
- property.setName(INCLUDE_IN_USERINFO);
- property.setLabel(INCLUDE_IN_USERINFO_LABEL);
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(INCLUDE_IN_USERINFO_HELP_TEXT);
- configProperties.add(property);
+ if (UserInfoTokenMapper.class.isAssignableFrom(protocolMapperClass)) {
+ ProviderConfigProperty property = new ProviderConfigProperty();
+ property.setName(INCLUDE_IN_USERINFO);
+ property.setLabel(INCLUDE_IN_USERINFO_LABEL);
+ property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
+ property.setDefaultValue("true");
+ property.setHelpText(INCLUDE_IN_USERINFO_HELP_TEXT);
+ configProperties.add(property);
+ }
}
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
index 7020e5f62e..e6d0d209f5 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
@@ -51,7 +51,7 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
- OIDCAttributeMapperHelper.addAttributeConfig(configProperties);
+ OIDCAttributeMapperHelper.addAttributeConfig(configProperties, UserAttributeMapper.class);
property = new ProviderConfigProperty();
property.setName(ProtocolMapperUtils.MULTIVALUED);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java
index dcb55a8fa3..01d47e13d7 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java
@@ -47,7 +47,7 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
clientId.setName(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID);
clientId.setLabel(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID_LABEL);
clientId.setHelpText(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID_HELP_TEXT);
- clientId.setType(ProviderConfigProperty.STRING_TYPE);
+ clientId.setType(ProviderConfigProperty.CLIENT_LIST_TYPE);
CONFIG_PROPERTIES.add(clientId);
ProviderConfigProperty clientRolePrefix = new ProviderConfigProperty();
@@ -57,7 +57,7 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
clientRolePrefix.setType(ProviderConfigProperty.STRING_TYPE);
CONFIG_PROPERTIES.add(clientRolePrefix);
- OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES);
+ OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES, UserClientRoleMappingMapper.class);
}
public List getConfigProperties() {
@@ -100,4 +100,21 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
OIDCAttributeMapperHelper.mapClaim(token, mappingModel, clientRoleNames);
}
}
+
+
+ public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
+ String name,
+ String tokenClaimName,
+ boolean accessToken, boolean idToken) {
+ ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
+ tokenClaimName, "String",
+ true, name,
+ accessToken, idToken,
+ PROVIDER_ID);
+
+ mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID, clientId);
+ mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_ROLE_PREFIX, clientRolePrefix);
+ return mapper;
+
+ }
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
index 54ba0963d0..6fd649199d 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
@@ -38,7 +38,7 @@ import java.util.List;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
+public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
private static final List configProperties = new ArrayList();
static {
@@ -49,7 +49,7 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT);
configProperties.add(property);
- OIDCAttributeMapperHelper.addAttributeConfig(configProperties);
+ OIDCAttributeMapperHelper.addAttributeConfig(configProperties, UserPropertyMapper.class);
}
public static final String PROVIDER_ID = "oidc-usermodel-property-mapper";
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java
index 33460708a9..ef9818227b 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java
@@ -21,11 +21,14 @@ import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -48,7 +51,7 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
realmRolePrefix.setType(ProviderConfigProperty.STRING_TYPE);
CONFIG_PROPERTIES.add(realmRolePrefix);
- OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES);
+ OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES, UserRealmRoleMappingMapper.class);
}
public List getConfigProperties() {
@@ -80,8 +83,23 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
UserModel user = userSession.getUser();
String rolePrefix = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX);
- Set realmRoleNames = flattenRoleModelToRoleNames(user.getRoleMappings(), rolePrefix);
+ Set realmRoleNames = flattenRoleModelToRoleNames(user.getRealmRoleMappings(), rolePrefix);
OIDCAttributeMapperHelper.mapClaim(token, mappingModel, realmRoleNames);
}
+
+
+ public static ProtocolMapperModel create(String realmRolePrefix,
+ String name,
+ String tokenClaimName, boolean accessToken, boolean idToken) {
+ ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
+ tokenClaimName, "String",
+ true, name,
+ accessToken, idToken,
+ PROVIDER_ID);
+
+ mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX, realmRolePrefix);
+ return mapper;
+
+ }
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
index e2615f9c0a..fd6bfe1c68 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
@@ -49,7 +49,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
property.setHelpText(ProtocolMapperUtils.USER_SESSION_MODEL_NOTE_HELP_TEXT);
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
- OIDCAttributeMapperHelper.addAttributeConfig(configProperties);
+ OIDCAttributeMapperHelper.addAttributeConfig(configProperties, UserSessionNoteMapper.class);
}
public static final String PROVIDER_ID = "oidc-usersessionmodel-note-mapper";
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java
index fc540231b4..fb39ec5457 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java
@@ -49,7 +49,7 @@ public class UserInfoClientUtil {
return client.target(userInfoUri);
}
- public static void testSuccessfulUserInfoResponse(Response response, String expectedUsername, String expectedEmail) {
+ public static UserInfo testSuccessfulUserInfoResponse(Response response, String expectedUsername, String expectedEmail) {
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
Assert.assertEquals(response.getHeaderString(HttpHeaders.CONTENT_TYPE), MediaType.APPLICATION_JSON);
@@ -61,6 +61,7 @@ public class UserInfoClientUtil {
Assert.assertNotNull(userInfo.getSubject());
Assert.assertEquals(expectedEmail, userInfo.getEmail());
Assert.assertEquals(expectedUsername, userInfo.getPreferredUsername());
+ return userInfo;
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java
index ca4553d69e..c5bb78577b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java
@@ -27,6 +27,7 @@ import org.keycloak.client.registration.ClientRegistrationException;
import org.keycloak.client.registration.HttpErrorException;
import org.keycloak.protocol.oidc.mappers.SHA256PairwiseSubMapper;
import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.UserInfo;
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@@ -38,7 +39,9 @@ import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
import org.keycloak.testsuite.client.resources.TestOIDCEndpointsApplicationResource;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.testsuite.util.UserInfoClientUtil;
+import javax.ws.rs.client.Client;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.Collections;
@@ -323,5 +326,17 @@ public class OIDCPairwiseClientRegistrationTest extends AbstractClientRegistrati
// Assert pairwise client has different subject like userId
String pairwiseUserId = accessToken.getSubject();
Assert.assertNotEquals(pairwiseUserId, user.getId());
+
+ // Send request to userInfo endpoint
+ Client jaxrsClient = javax.ws.rs.client.ClientBuilder.newClient();
+ try {
+ // Check that userInfo contains pairwise subjectId as well
+ Response userInfoResponse = UserInfoClientUtil.executeUserInfoRequest_getMethod(jaxrsClient, accessTokenResponse.getAccessToken());
+ UserInfo userInfo = UserInfoClientUtil.testSuccessfulUserInfoResponse(userInfoResponse, "test-user", "test-user@localhost");
+ String userInfoSubId = userInfo.getSubject();
+ Assert.assertEquals(pairwiseUserId, userInfoSubId);
+ } finally {
+ jaxrsClient.close();
+ }
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index 13c1ea45ad..eaeeafd9c1 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -673,138 +673,6 @@ public class AccessTokenTest extends AbstractKeycloakTest {
}
- @Test
- public void testTokenMapping() throws Exception {
- Client client = javax.ws.rs.client.ClientBuilder.newClient();
- UriBuilder builder = UriBuilder.fromUri(AUTH_SERVER_ROOT);
- URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
- WebTarget grantTarget = client.target(grantUri);
- {
- UserResource userResource = findUserByUsernameId(adminClient.realm("test"), "test-user@localhost");
- UserRepresentation user = userResource.toRepresentation();
-
- user.singleAttribute("street", "5 Yawkey Way");
- user.singleAttribute("locality", "Boston");
- user.singleAttribute("region", "MA");
- user.singleAttribute("postal_code", "02115");
- user.singleAttribute("country", "USA");
- user.singleAttribute("phone", "617-777-6666");
-
- List departments = Arrays.asList("finance", "development");
- user.getAttributes().put("departments", departments);
- userResource.update(user);
-
- ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
-
- ProtocolMapperRepresentation mapper = createAddressMapper(true, true);
- app.getProtocolMappers().createMapper(mapper);
-
- ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", false, null, true, true);
- app.getProtocolMappers().createMapper(hard);
- app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", false, null, true, true));
- app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, "", true, true, false));
- app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, "", true, true, false));
- app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, "", true, true, true));
- app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded"));
- app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded"));
- app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user"));
- }
-
- {
- Response response = executeGrantAccessTokenRequest(grantTarget);
- assertEquals(200, response.getStatus());
-
- org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class);
- IDToken idToken = getIdToken(tokenResponse);
- assertNotNull(idToken.getAddress());
- assertEquals(idToken.getName(), "Tom Brady");
- assertEquals(idToken.getAddress().getStreetAddress(), "5 Yawkey Way");
- assertEquals(idToken.getAddress().getLocality(), "Boston");
- assertEquals(idToken.getAddress().getRegion(), "MA");
- assertEquals(idToken.getAddress().getPostalCode(), "02115");
- assertEquals(idToken.getAddress().getCountry(), "USA");
- assertNotNull(idToken.getOtherClaims().get("home_phone"));
- assertEquals("617-777-6666", idToken.getOtherClaims().get("home_phone"));
- assertEquals("coded", idToken.getOtherClaims().get("hard"));
- Map nested = (Map) idToken.getOtherClaims().get("nested");
- assertEquals("coded-nested", nested.get("hard"));
- nested = (Map) idToken.getOtherClaims().get("home");
- assertEquals("617-777-6666", nested.get("phone"));
- List departments = (List) idToken.getOtherClaims().get("department");
- assertEquals(2, departments.size());
- assertTrue(departments.contains("finance") && departments.contains("development"));
-
- AccessToken accessToken = getAccessToken(tokenResponse);
- assertEquals(accessToken.getName(), "Tom Brady");
- assertNotNull(accessToken.getAddress());
- assertEquals(accessToken.getAddress().getStreetAddress(), "5 Yawkey Way");
- assertEquals(accessToken.getAddress().getLocality(), "Boston");
- assertEquals(accessToken.getAddress().getRegion(), "MA");
- assertEquals(accessToken.getAddress().getPostalCode(), "02115");
- assertEquals(accessToken.getAddress().getCountry(), "USA");
- assertNotNull(accessToken.getOtherClaims().get("home_phone"));
- assertEquals("617-777-6666", accessToken.getOtherClaims().get("home_phone"));
- assertEquals("coded", accessToken.getOtherClaims().get("hard"));
- nested = (Map) accessToken.getOtherClaims().get("nested");
- assertEquals("coded-nested", nested.get("hard"));
- nested = (Map) accessToken.getOtherClaims().get("home");
- assertEquals("617-777-6666", nested.get("phone"));
- departments = (List) idToken.getOtherClaims().get("department");
- assertEquals(2, departments.size());
- assertTrue(departments.contains("finance") && departments.contains("development"));
- assertTrue(accessToken.getRealmAccess().getRoles().contains("hardcoded"));
- assertTrue(accessToken.getRealmAccess().getRoles().contains("realm-user"));
- Assert.assertFalse(accessToken.getResourceAccess("test-app").getRoles().contains("customer-user"));
- assertTrue(accessToken.getResourceAccess("app").getRoles().contains("hardcoded"));
-
-
- response.close();
- }
-
- // undo mappers
- {
- ClientResource app = findClientByClientId(adminClient.realm("test"), "test-app");
- ClientRepresentation clientRepresentation = app.toRepresentation();
- for (ProtocolMapperRepresentation model : clientRepresentation.getProtocolMappers()) {
- if (model.getName().equals("address")
- || model.getName().equals("hard")
- || model.getName().equals("hard-nested")
- || model.getName().equals("custom phone")
- || model.getName().equals("departments")
- || model.getName().equals("nested phone")
- || model.getName().equals("rename-app-role")
- || model.getName().equals("hard-realm")
- || model.getName().equals("hard-app")
- ) {
- app.getProtocolMappers().delete(model.getId());
- }
- }
- }
-
- events.clear();
-
-
- {
- Response response = executeGrantAccessTokenRequest(grantTarget);
- assertEquals(200, response.getStatus());
- org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class);
- IDToken idToken = getIdToken(tokenResponse);
- assertNull(idToken.getAddress());
- assertNull(idToken.getOtherClaims().get("home_phone"));
- assertNull(idToken.getOtherClaims().get("hard"));
- assertNull(idToken.getOtherClaims().get("nested"));
- assertNull(idToken.getOtherClaims().get("department"));
-
- response.close();
- }
-
-
- events.clear();
- client.close();
-
-
- }
-
@Test
public void testClientTemplate() throws Exception {
RealmResource realm = adminClient.realm("test");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java
new file mode 100644
index 0000000000..d317af7de1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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.testsuite.oauth;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ProtocolMappersResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.IDToken;
+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.Assert;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.util.ClientManager;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.testsuite.util.ProtocolMapperUtil;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
+import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
+import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId;
+import static org.keycloak.testsuite.admin.ApiUtil.findUserByUsernameId;
+import static org.keycloak.testsuite.util.ProtocolMapperUtil.createAddressMapper;
+import static org.keycloak.testsuite.util.ProtocolMapperUtil.createClaimMapper;
+import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim;
+import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedRole;
+import static org.keycloak.testsuite.util.ProtocolMapperUtil.createRoleNameMapper;
+
+/**
+ * @author Marek Posolda
+ */
+public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
+
+ @Rule
+ public AssertEvents events = new AssertEvents(this);
+
+
+ @Override
+ public void beforeAbstractKeycloakTest() throws Exception {
+ super.beforeAbstractKeycloakTest();
+ }
+
+ @Before
+ public void clientConfiguration() {
+ ClientManager.realm(adminClient.realm("test")).clientId("test-app").directAccessGrant(true);
+ /*
+ * Configure the default client ID. Seems like OAuthClient is keeping the state of clientID
+ * For example: If some test case configure oauth.clientId("sample-public-client"), other tests
+ * will faile and the clientID will always be "sample-public-client
+ * @see AccessTokenTest#testAuthorizationNegotiateHeaderIgnored()
+ */
+ oauth.clientId("test-app");
+ }
+
+ @Override
+ public void addTestRealms(List testRealms) {
+ RealmRepresentation realm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
+ testRealms.add(realm);
+ }
+
+
+ @Test
+ public void testTokenMapping() throws Exception {
+ {
+ UserResource userResource = findUserByUsernameId(adminClient.realm("test"), "test-user@localhost");
+ UserRepresentation user = userResource.toRepresentation();
+
+ user.singleAttribute("street", "5 Yawkey Way");
+ user.singleAttribute("locality", "Boston");
+ user.singleAttribute("region", "MA");
+ user.singleAttribute("postal_code", "02115");
+ user.singleAttribute("country", "USA");
+ user.singleAttribute("phone", "617-777-6666");
+
+ List departments = Arrays.asList("finance", "development");
+ user.getAttributes().put("departments", departments);
+ userResource.update(user);
+
+ ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
+
+ ProtocolMapperRepresentation mapper = createAddressMapper(true, true);
+ app.getProtocolMappers().createMapper(mapper);
+
+ ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", false, null, true, true);
+ app.getProtocolMappers().createMapper(hard);
+ app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", false, null, true, true));
+ app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, "", true, true, false));
+ app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, "", true, true, false));
+ app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, "", true, true, true));
+ app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded"));
+ app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded"));
+ app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user"));
+ }
+
+ {
+ OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
+
+ IDToken idToken = oauth.verifyIDToken(response.getIdToken());
+ assertNotNull(idToken.getAddress());
+ assertEquals(idToken.getName(), "Tom Brady");
+ assertEquals(idToken.getAddress().getStreetAddress(), "5 Yawkey Way");
+ assertEquals(idToken.getAddress().getLocality(), "Boston");
+ assertEquals(idToken.getAddress().getRegion(), "MA");
+ assertEquals(idToken.getAddress().getPostalCode(), "02115");
+ assertEquals(idToken.getAddress().getCountry(), "USA");
+ assertNotNull(idToken.getOtherClaims().get("home_phone"));
+ assertEquals("617-777-6666", idToken.getOtherClaims().get("home_phone"));
+ assertEquals("coded", idToken.getOtherClaims().get("hard"));
+ Map nested = (Map) idToken.getOtherClaims().get("nested");
+ assertEquals("coded-nested", nested.get("hard"));
+ nested = (Map) idToken.getOtherClaims().get("home");
+ assertEquals("617-777-6666", nested.get("phone"));
+ List departments = (List) idToken.getOtherClaims().get("department");
+ assertEquals(2, departments.size());
+ assertTrue(departments.contains("finance") && departments.contains("development"));
+
+ AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
+ assertEquals(accessToken.getName(), "Tom Brady");
+ assertNotNull(accessToken.getAddress());
+ assertEquals(accessToken.getAddress().getStreetAddress(), "5 Yawkey Way");
+ assertEquals(accessToken.getAddress().getLocality(), "Boston");
+ assertEquals(accessToken.getAddress().getRegion(), "MA");
+ assertEquals(accessToken.getAddress().getPostalCode(), "02115");
+ assertEquals(accessToken.getAddress().getCountry(), "USA");
+ assertNotNull(accessToken.getOtherClaims().get("home_phone"));
+ assertEquals("617-777-6666", accessToken.getOtherClaims().get("home_phone"));
+ assertEquals("coded", accessToken.getOtherClaims().get("hard"));
+ nested = (Map) accessToken.getOtherClaims().get("nested");
+ assertEquals("coded-nested", nested.get("hard"));
+ nested = (Map) accessToken.getOtherClaims().get("home");
+ assertEquals("617-777-6666", nested.get("phone"));
+ departments = (List) idToken.getOtherClaims().get("department");
+ assertEquals(2, departments.size());
+ assertTrue(departments.contains("finance") && departments.contains("development"));
+ assertTrue(accessToken.getRealmAccess().getRoles().contains("hardcoded"));
+ assertTrue(accessToken.getRealmAccess().getRoles().contains("realm-user"));
+ Assert.assertFalse(accessToken.getResourceAccess("test-app").getRoles().contains("customer-user"));
+ assertTrue(accessToken.getResourceAccess("app").getRoles().contains("hardcoded"));
+ }
+
+ // undo mappers
+ {
+ ClientResource app = findClientByClientId(adminClient.realm("test"), "test-app");
+ ClientRepresentation clientRepresentation = app.toRepresentation();
+ for (ProtocolMapperRepresentation model : clientRepresentation.getProtocolMappers()) {
+ if (model.getName().equals("address")
+ || model.getName().equals("hard")
+ || model.getName().equals("hard-nested")
+ || model.getName().equals("custom phone")
+ || model.getName().equals("departments")
+ || model.getName().equals("nested phone")
+ || model.getName().equals("rename-app-role")
+ || model.getName().equals("hard-realm")
+ || model.getName().equals("hard-app")
+ ) {
+ app.getProtocolMappers().delete(model.getId());
+ }
+ }
+ }
+
+ events.clear();
+
+
+ {
+ OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
+ IDToken idToken = oauth.verifyIDToken(response.getIdToken());
+ assertNull(idToken.getAddress());
+ assertNull(idToken.getOtherClaims().get("home_phone"));
+ assertNull(idToken.getOtherClaims().get("hard"));
+ assertNull(idToken.getOtherClaims().get("nested"));
+ assertNull(idToken.getOtherClaims().get("department"));
+ }
+
+
+ events.clear();
+ }
+
+
+ @Test
+ 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);
+
+ ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
+ protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
+
+ // Login user
+ OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
+ IDToken idToken = oauth.verifyIDToken(response.getIdToken());
+
+ // Verify attribute is filled
+ Map roleMappings = (Map)idToken.getOtherClaims().get("roles-custom");
+ Assert.assertEquals(2, roleMappings.size());
+ String realmRoleMappings = (String) roleMappings.get("realm");
+ String testAppMappings = (String) roleMappings.get("test-app");
+ Assert.assertTrue(realmRoleMappings.contains("pref.user"));
+ Assert.assertEquals("[customer-user]", testAppMappings);
+ }
+
+
+ private ProtocolMapperRepresentation makeMapper(String name, String mapperType, Map config) {
+ ProtocolMapperRepresentation rep = new ProtocolMapperRepresentation();
+ rep.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ rep.setName(name);
+ rep.setProtocolMapper(mapperType);
+ rep.setConfig(config);
+ rep.setConsentRequired(true);
+ rep.setConsentText("Test Consent Text");
+ return rep;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java
index 62018e1c0e..6bcea06cd2 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java
@@ -6,6 +6,8 @@ import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
import org.keycloak.protocol.oidc.mappers.HardcodedRole;
import org.keycloak.protocol.oidc.mappers.RoleNameMapper;
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
+import org.keycloak.protocol.oidc.mappers.UserClientRoleMappingMapper;
+import org.keycloak.protocol.oidc.mappers.UserRealmRoleMappingMapper;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@@ -103,4 +105,22 @@ public class ProtocolMapperUtil {
consentRequired, consentText,
accessToken, idToken));
}
+
+
+ public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
+ String name,
+ String tokenClaimName,
+ boolean accessToken, boolean idToken) {
+
+ return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken));
+ }
+
+
+ public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
+ String name,
+ String tokenClaimName,
+ boolean accessToken, boolean idToken) {
+
+ return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken));
+ }
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
index 9cad056222..ed1e757228 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
@@ -242,7 +242,7 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent
* Test for KEYCLOAK-1053 - verify email action is not performed if email is not provided, login is normal, but action stays in set to be performed later
*/
@Test
- public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() {
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() throws Exception {
RealmModel realm = getRealm();
realm.setVerifyEmail(true);
setUpdateProfileFirstLogin(realm, IdentityProviderRepresentation.UPFLM_OFF);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
index 0552875a4e..58c4ca18f3 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
@@ -151,7 +151,7 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityP
}
@Test
- public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() {
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() throws Exception {
super.testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled();
}
diff --git a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json
index 767ab44a3b..05cb0e9340 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json
@@ -26,8 +26,8 @@
"claim.name": "mobile",
"Claim JSON Type": "String",
"access.token.claim": "true",
- "id.token.claim": "true"
-
+ "id.token.claim": "true",
+ "userinfo.token.claim": "true"
}
},
{
@@ -40,8 +40,8 @@
"claim.name": "preferred_username",
"Claim JSON Type": "String",
"access.token.claim": "true",
- "id.token.claim": "true"
-
+ "id.token.claim": "true",
+ "userinfo.token.claim": "true"
}
},
{
@@ -54,8 +54,8 @@
"claim.name": "email",
"Claim JSON Type": "String",
"access.token.claim": "true",
- "id.token.claim": "true"
-
+ "id.token.claim": "true",
+ "userinfo.token.claim": "true"
}
},
{
@@ -68,8 +68,8 @@
"claim.name": "given_name",
"Claim JSON Type": "String",
"access.token.claim": "true",
- "id.token.claim": "true"
-
+ "id.token.claim": "true",
+ "userinfo.token.claim": "true"
}
},
{
@@ -83,7 +83,6 @@
"Claim JSON Type": "String",
"access.token.claim": "true",
"id.token.claim": "true"
-
}
},
{
@@ -93,8 +92,8 @@
"consentRequired": false,
"config": {
"access.token.claim": "true",
- "id.token.claim": "true"
-
+ "id.token.claim": "true",
+ "userinfo.token.claim": "true"
}
}
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js
index e1f9891895..e892b4544b 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -953,6 +953,9 @@ module.config([ '$routeProvider', function($routeProvider) {
},
mapper : function(ClientProtocolMapperLoader) {
return ClientProtocolMapperLoader();
+ },
+ clients : function(ClientListLoader) {
+ return ClientListLoader();
}
},
@@ -969,6 +972,9 @@ module.config([ '$routeProvider', function($routeProvider) {
},
client : function(ClientLoader) {
return ClientLoader();
+ },
+ clients : function(ClientListLoader) {
+ return ClientListLoader();
}
},
controller : 'ClientProtocolMapperCreateCtrl'
@@ -1017,6 +1023,9 @@ module.config([ '$routeProvider', function($routeProvider) {
},
mapper : function(ClientTemplateProtocolMapperLoader) {
return ClientTemplateProtocolMapperLoader();
+ },
+ clients : function(ClientListLoader) {
+ return ClientListLoader();
}
},
@@ -1033,6 +1042,9 @@ module.config([ '$routeProvider', function($routeProvider) {
},
template : function(ClientTemplateLoader) {
return ClientTemplateLoader();
+ },
+ clients : function(ClientListLoader) {
+ return ClientListLoader();
}
},
controller : 'ClientTemplateProtocolMapperCreateCtrl'
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index 2de4453e32..3405791c03 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -1705,8 +1705,10 @@ module.controller('ClientProtocolMapperListCtrl', function($scope, realm, client
updateMappers();
});
-module.controller('ClientProtocolMapperCtrl', function($scope, realm, serverInfo, client, mapper, ClientProtocolMapper, Notifications, Dialog, $location) {
+module.controller('ClientProtocolMapperCtrl', function($scope, realm, serverInfo, client, clients, mapper, ClientProtocolMapper, Notifications, Dialog, $location) {
$scope.realm = realm;
+ $scope.clients = clients;
+
/*
$scope.client = client;
$scope.create = false;
@@ -1786,8 +1788,9 @@ module.controller('ClientProtocolMapperCtrl', function($scope, realm, serverInfo
});
-module.controller('ClientProtocolMapperCreateCtrl', function($scope, realm, serverInfo, client, ClientProtocolMapper, Notifications, Dialog, $location) {
+module.controller('ClientProtocolMapperCreateCtrl', function($scope, realm, serverInfo, client, clients, ClientProtocolMapper, Notifications, Dialog, $location) {
$scope.realm = realm;
+ $scope.clients = clients;
if (client.protocol == null) {
client.protocol = 'openid-connect';
@@ -2001,8 +2004,10 @@ module.controller('ClientTemplateProtocolMapperListCtrl', function($scope, realm
updateMappers();
});
-module.controller('ClientTemplateProtocolMapperCtrl', function($scope, realm, serverInfo, template, mapper, ClientTemplateProtocolMapper, Notifications, Dialog, $location) {
+module.controller('ClientTemplateProtocolMapperCtrl', function($scope, realm, serverInfo, template, mapper, clients, ClientTemplateProtocolMapper, Notifications, Dialog, $location) {
$scope.realm = realm;
+ $scope.clients = clients;
+
if (template.protocol == null) {
template.protocol = 'openid-connect';
}
@@ -2068,8 +2073,10 @@ module.controller('ClientTemplateProtocolMapperCtrl', function($scope, realm, se
});
-module.controller('ClientTemplateProtocolMapperCreateCtrl', function($scope, realm, serverInfo, template, ClientTemplateProtocolMapper, Notifications, Dialog, $location) {
+module.controller('ClientTemplateProtocolMapperCreateCtrl', function($scope, realm, serverInfo, template, clients, ClientTemplateProtocolMapper, Notifications, Dialog, $location) {
$scope.realm = realm;
+ $scope.clients = clients;
+
if (template.protocol == null) {
template.protocol = 'openid-connect';
}
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
index fe781c22db..e630bf981a 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
@@ -60,7 +60,7 @@
{{model.mapperType.helpText}}
-
+