keycloak-aplcache
Changes
services/pom.xml 5(+5 -0)
services/src/test/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelperTest.java 44(+44 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java 6(+6 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java 28(+27 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java 22(+21 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserAttributeMapperTest.java 18(+17 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SamlUserAttributeMapperTest.java 18(+17 -1)
Details
services/pom.xml 5(+5 -0)
diff --git a/services/pom.xml b/services/pom.xml
index 15233a6..77f3597 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -174,6 +174,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
<scope>test</scope>
diff --git a/services/src/main/java/org/keycloak/broker/oidc/mappers/AbstractClaimMapper.java b/services/src/main/java/org/keycloak/broker/oidc/mappers/AbstractClaimMapper.java
index f2b5288..a5bbbbe 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/mappers/AbstractClaimMapper.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/mappers/AbstractClaimMapper.java
@@ -23,10 +23,14 @@ import org.keycloak.broker.oidc.OIDCIdentityProvider;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.models.IdentityProviderMapperModel;
+import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.representations.JsonWebToken;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -37,13 +41,16 @@ public abstract class AbstractClaimMapper extends AbstractIdentityProviderMapper
public static final String CLAIM_VALUE = "claim.value";
public static Object getClaimValue(JsonWebToken token, String claim) {
- String[] split = claim.split("\\.");
+ List<String> split = OIDCAttributeMapperHelper.splitClaimPath(claim);
Map<String, Object> jsonObject = token.getOtherClaims();
- for (int i = 0; i < split.length; i++) {
- if (i == split.length - 1) {
- return jsonObject.get(split[i]);
+ final int length = split.size();
+ int i = 0;
+ for (String component : split) {
+ i++;
+ if (i == length) {
+ return jsonObject.get(component);
} else {
- Object val = jsonObject.get(split[i]);
+ Object val = jsonObject.get(component);
if (!(val instanceof Map)) return null;
jsonObject = (Map<String, Object>)val;
}
diff --git a/services/src/main/java/org/keycloak/broker/oidc/mappers/ClaimToRoleMapper.java b/services/src/main/java/org/keycloak/broker/oidc/mappers/ClaimToRoleMapper.java
index e026623..dcb1622 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/mappers/ClaimToRoleMapper.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/mappers/ClaimToRoleMapper.java
@@ -49,7 +49,7 @@ public class ClaimToRoleMapper extends AbstractClaimMapper {
property1 = new ProviderConfigProperty();
property1.setName(CLAIM);
property1.setLabel("Claim");
- property1.setHelpText("Name of claim to search for in token. You can reference nested claims using a '.', i.e. 'address.locality'.");
+ property1.setHelpText("Name of claim to search for in token. You can reference nested claims using a '.', i.e. 'address.locality'. To use dot (.) literally, escape it with backslash (\\.)");
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property1 = new ProviderConfigProperty();
diff --git a/services/src/main/java/org/keycloak/broker/oidc/mappers/UserAttributeMapper.java b/services/src/main/java/org/keycloak/broker/oidc/mappers/UserAttributeMapper.java
index 2e2abca..4770cf3 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/mappers/UserAttributeMapper.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/mappers/UserAttributeMapper.java
@@ -56,7 +56,7 @@ public class UserAttributeMapper extends AbstractClaimMapper {
property1 = new ProviderConfigProperty();
property1.setName(CLAIM);
property1.setLabel("Claim");
- property1.setHelpText("Name of claim to search for in token. You can reference nested claims using a '.', i.e. 'address.locality'.");
+ property1.setHelpText("Name of claim to search for in token. You can reference nested claims using a '.', i.e. 'address.locality'. To use dot (.) literally, escape it with backslash (\\.)");
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property = new ProviderConfigProperty();
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 0365bc3..933654c 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
@@ -27,6 +27,8 @@ import org.keycloak.services.ServicesLogger;
import java.util.*;
import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
@@ -143,6 +145,28 @@ public class OIDCAttributeMapperHelper {
return null;
}
+ // A character in a claim component is either a literal character escaped by a backslash (\., \\, \_, \q, etc.)
+ // or any character other than backslash (escaping) and dot (claim component separator)
+ private static final Pattern CLAIM_COMPONENT = Pattern.compile("^((\\\\.|[^\\\\.])+?)\\.");
+
+ private static final Pattern BACKSLASHED_CHARACTER = Pattern.compile("\\\\(.)");
+
+ public static List<String> splitClaimPath(String claimPath) {
+ final LinkedList<String> claimComponents = new LinkedList<>();
+ Matcher m = CLAIM_COMPONENT.matcher(claimPath);
+ int start = 0;
+ while (m.find()) {
+ claimComponents.add(BACKSLASHED_CHARACTER.matcher(m.group(1)).replaceAll("$1"));
+ start = m.end();
+ // This is necessary to match the start of region as the start of string as determined by ^
+ m.region(start, claimPath.length());
+ }
+ if (claimPath.length() > start) {
+ claimComponents.add(BACKSLASHED_CHARACTER.matcher(claimPath.substring(start)).replaceAll("$1"));
+ }
+ return claimComponents;
+ }
+
public static void mapClaim(IDToken token, ProtocolMapperModel mappingModel, Object attributeValue) {
attributeValue = mapAttributeValue(mappingModel, attributeValue);
if (attributeValue == null) return;
@@ -151,17 +175,20 @@ public class OIDCAttributeMapperHelper {
if (protocolClaim == null) {
return;
}
- String[] split = protocolClaim.split("\\.");
+ List<String> split = splitClaimPath(protocolClaim);
+ final int length = split.size();
+ int i = 0;
Map<String, Object> jsonObject = token.getOtherClaims();
- for (int i = 0; i < split.length; i++) {
- if (i == split.length - 1) {
- jsonObject.put(split[i], attributeValue);
+ for (String component : split) {
+ i++;
+ if (i == length) {
+ jsonObject.put(component, attributeValue);
} else {
- Map<String, Object> nested = (Map<String, Object>)jsonObject.get(split[i]);
+ Map<String, Object> nested = (Map<String, Object>)jsonObject.get(component);
if (nested == null) {
nested = new HashMap<String, Object>();
- jsonObject.put(split[i], nested);
+ jsonObject.put(component, nested);
}
jsonObject = nested;
diff --git a/services/src/test/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelperTest.java b/services/src/test/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelperTest.java
new file mode 100644
index 0000000..438544c
--- /dev/null
+++ b/services/src/test/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelperTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 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.protocol.oidc.mappers;
+
+import org.hamcrest.Matchers;
+import org.junit.Test;
+import static org.junit.Assert.assertThat;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class OIDCAttributeMapperHelperTest {
+
+ @Test
+ public void testSplitClaimPath() {
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath(""), Matchers.empty());
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath("a"), Matchers.contains("a"));
+
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath("a.b"), Matchers.contains("a", "b"));
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath("a\\.b"), Matchers.contains("a.b"));
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath("a\\\\.b"), Matchers.contains("a\\", "b"));
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath("a\\\\\\.b"), Matchers.contains("a\\.b"));
+
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath("c.a\\\\.b"), Matchers.contains("c", "a\\", "b"));
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath("c.a\\\\\\.b"), Matchers.contains("c", "a\\.b"));
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath("c\\\\\\.b.a\\\\\\.b"), Matchers.contains("c\\.b", "a\\.b"));
+ assertThat(OIDCAttributeMapperHelper.splitClaimPath("c\\h\\.b.a\\\\\\.b"), Matchers.contains("ch.b", "a\\.b"));
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java
index 045aeb9..4f3b29e 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java
@@ -37,6 +37,8 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
private static final Set<String> PROTECTED_NAMES = ImmutableSet.<String>builder().add("email").add("lastName").add("firstName").build();
private static final Map<String, String> ATTRIBUTE_NAME_TRANSLATION = ImmutableMap.<String, String>builder()
+ .put("dotted.email", "dotted.email")
+ .put("nested.email", "nested.email")
.put(ATTRIBUTE_TO_MAP_FRIENDLY_NAME, MAPPED_ATTRIBUTE_FRIENDLY_NAME)
.put(ATTRIBUTE_TO_MAP_NAME, MAPPED_ATTRIBUTE_NAME)
.build();
@@ -198,9 +200,13 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker
public void testBasicMappingEmail() {
testValueMapping(ImmutableMap.<String, List<String>>builder()
.put("email", ImmutableList.<String>builder().add(bc.getUserEmail()).build())
+ .put("nested.email", ImmutableList.<String>builder().add(bc.getUserEmail()).build())
+ .put("dotted.email", ImmutableList.<String>builder().add(bc.getUserEmail()).build())
.build(),
ImmutableMap.<String, List<String>>builder()
.put("email", ImmutableList.<String>builder().add("other_email@redhat.com").build())
+ .put("nested.email", ImmutableList.<String>builder().add("other_email@redhat.com").build())
+ .put("dotted.email", ImmutableList.<String>builder().add("other_email@redhat.com").build())
.build()
);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java
index ce92201..c6da797 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java
@@ -74,6 +74,32 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
emailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
emailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
+ ProtocolMapperRepresentation nestedAttrMapper = new ProtocolMapperRepresentation();
+ nestedAttrMapper.setName("attribute - nested claim");
+ nestedAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ nestedAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
+
+ Map<String, String> nestedEmailMapperConfig = nestedAttrMapper.getConfig();
+ nestedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "nested.email");
+ nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "nested.email");
+ nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE);
+ nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+ nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+ nestedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
+
+ ProtocolMapperRepresentation dottedAttrMapper = new ProtocolMapperRepresentation();
+ dottedAttrMapper.setName("attribute - claim with dot in name");
+ dottedAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ dottedAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
+
+ Map<String, String> dottedEmailMapperConfig = dottedAttrMapper.getConfig();
+ dottedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "dotted.email");
+ dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "dotted\\.email");
+ dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE);
+ dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+ dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+ dottedEmailMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
+
ProtocolMapperRepresentation userAttrMapper = new ProtocolMapperRepresentation();
userAttrMapper.setName("attribute - name");
userAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
@@ -88,7 +114,7 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
userAttrMapperConfig.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
userAttrMapperConfig.put(ProtocolMapperUtils.MULTIVALUED, "true");
- client.setProtocolMappers(Arrays.asList(emailMapper, userAttrMapper));
+ client.setProtocolMappers(Arrays.asList(emailMapper, userAttrMapper, nestedAttrMapper, dottedAttrMapper));
return Collections.singletonList(client);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
index eabeec8..904acd6 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
@@ -97,6 +97,26 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
emailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");
emailMapperConfig.put(AttributeStatementHelper.FRIENDLY_NAME, "email");
+ ProtocolMapperRepresentation dottedAttrMapper = new ProtocolMapperRepresentation();
+ dottedAttrMapper.setName("email - dotted");
+ dottedAttrMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
+ dottedAttrMapper.setProtocolMapper(UserAttributeStatementMapper.PROVIDER_ID);
+
+ Map<String, String> dottedEmailMapperConfig = dottedAttrMapper.getConfig();
+ dottedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "dotted.email");
+ dottedEmailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "dotted.email");
+ dottedEmailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");
+
+ ProtocolMapperRepresentation nestedAttrMapper = new ProtocolMapperRepresentation();
+ nestedAttrMapper.setName("email - nested");
+ nestedAttrMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
+ nestedAttrMapper.setProtocolMapper(UserAttributeStatementMapper.PROVIDER_ID);
+
+ Map<String, String> nestedEmailMapperConfig = nestedAttrMapper.getConfig();
+ nestedEmailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "nested.email");
+ nestedEmailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "nested.email");
+ nestedEmailMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");
+
ProtocolMapperRepresentation userAttrMapper = new ProtocolMapperRepresentation();
userAttrMapper.setName("attribute - name");
userAttrMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
@@ -119,7 +139,7 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
userFriendlyAttrMapperConfig.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, AttributeStatementHelper.BASIC);
userFriendlyAttrMapperConfig.put(AttributeStatementHelper.FRIENDLY_NAME, AbstractUserAttributeMapperTest.ATTRIBUTE_TO_MAP_FRIENDLY_NAME);
- client.setProtocolMappers(Arrays.asList(emailMapper, userAttrMapper, userFriendlyAttrMapper));
+ client.setProtocolMappers(Arrays.asList(emailMapper, dottedAttrMapper, nestedAttrMapper, userAttrMapper, userFriendlyAttrMapper));
return client;
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserAttributeMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserAttributeMapperTest.java
index 0927a86..b5c3063 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserAttributeMapperTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcUserAttributeMapperTest.java
@@ -32,7 +32,23 @@ public class OidcUserAttributeMapperTest extends AbstractUserAttributeMapperTest
.put(UserAttributeMapper.USER_ATTRIBUTE, "email")
.build());
- return Lists.newArrayList(attrMapper1, emailAttrMapper);
+ IdentityProviderMapperRepresentation nestedEmailAttrMapper = new IdentityProviderMapperRepresentation();
+ nestedEmailAttrMapper.setName("nested-attribute-mapper-email");
+ nestedEmailAttrMapper.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
+ nestedEmailAttrMapper.setConfig(ImmutableMap.<String,String>builder()
+ .put(UserAttributeMapper.CLAIM, "nested.email")
+ .put(UserAttributeMapper.USER_ATTRIBUTE, "nested.email")
+ .build());
+
+ IdentityProviderMapperRepresentation dottedEmailAttrMapper = new IdentityProviderMapperRepresentation();
+ dottedEmailAttrMapper.setName("dotted-attribute-mapper-email");
+ dottedEmailAttrMapper.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
+ dottedEmailAttrMapper.setConfig(ImmutableMap.<String,String>builder()
+ .put(UserAttributeMapper.CLAIM, "dotted\\.email")
+ .put(UserAttributeMapper.USER_ATTRIBUTE, "dotted.email")
+ .build());
+
+ return Lists.newArrayList(attrMapper1, emailAttrMapper, nestedEmailAttrMapper, dottedEmailAttrMapper);
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SamlUserAttributeMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SamlUserAttributeMapperTest.java
index 7184ef8..7d01ef7 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SamlUserAttributeMapperTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SamlUserAttributeMapperTest.java
@@ -24,6 +24,22 @@ public class SamlUserAttributeMapperTest extends AbstractUserAttributeMapperTest
.put(UserAttributeMapper.USER_ATTRIBUTE, "email")
.build());
+ IdentityProviderMapperRepresentation attrMapperNestedEmail = new IdentityProviderMapperRepresentation();
+ attrMapperNestedEmail.setName("nested-attribute-mapper-email");
+ attrMapperNestedEmail.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
+ attrMapperNestedEmail.setConfig(ImmutableMap.<String,String>builder()
+ .put(UserAttributeMapper.ATTRIBUTE_NAME, "nested.email")
+ .put(UserAttributeMapper.USER_ATTRIBUTE, "nested.email")
+ .build());
+
+ IdentityProviderMapperRepresentation attrMapperDottedEmail = new IdentityProviderMapperRepresentation();
+ attrMapperDottedEmail.setName("dotted-attribute-mapper-email");
+ attrMapperDottedEmail.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
+ attrMapperDottedEmail.setConfig(ImmutableMap.<String,String>builder()
+ .put(UserAttributeMapper.ATTRIBUTE_NAME, "dotted.email")
+ .put(UserAttributeMapper.USER_ATTRIBUTE, "dotted.email")
+ .build());
+
IdentityProviderMapperRepresentation attrMapper1 = new IdentityProviderMapperRepresentation();
attrMapper1.setName("attribute-mapper");
attrMapper1.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
@@ -40,7 +56,7 @@ public class SamlUserAttributeMapperTest extends AbstractUserAttributeMapperTest
.put(UserAttributeMapper.USER_ATTRIBUTE, MAPPED_ATTRIBUTE_FRIENDLY_NAME)
.build());
- return Lists.newArrayList(attrMapperEmail, attrMapper1, attrMapper2);
+ return Lists.newArrayList(attrMapperEmail, attrMapper1, attrMapper2, attrMapperDottedEmail, attrMapperNestedEmail);
}
}
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
index f3e8ade..4e19606 100644
--- 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
@@ -143,6 +143,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
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(createHardcodedRole("hard-realm", "hardcoded")).close();
@@ -170,6 +171,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
assertEquals(idToken.getAddress().getFormattedAddress(), "6 Foo Street");
assertNotNull(idToken.getOtherClaims().get("home_phone"));
assertThat((List<String>) idToken.getOtherClaims().get("home_phone"), hasItems("617-777-6666"));
+ assertNotNull(idToken.getOtherClaims().get("home.phone"));
+ assertThat((List<String>) idToken.getOtherClaims().get("home.phone"), hasItems("617-777-6666"));
assertEquals("coded", idToken.getOtherClaims().get("hard"));
Map nested = (Map) idToken.getOtherClaims().get("nested");
assertEquals("coded-nested", nested.get("hard"));
@@ -221,6 +224,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|| model.getName().equals("hard")
|| model.getName().equals("hard-nested")
|| model.getName().equals("custom phone")
+ || model.getName().equals("dotted phone")
|| model.getName().equals("departments")
|| model.getName().equals("firstDepartment")
|| model.getName().equals("nested phone")
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 7572350..4924faa 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -202,7 +202,7 @@ multivalued.tooltip=Indicates if attribute supports multiple values. If true, th
selectRole.label=Select Role
selectRole.tooltip=Enter role in the textbox to the left, or click this button to browse and select the role you want.
tokenClaimName.label=Token Claim Name
-tokenClaimName.tooltip=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.
+tokenClaimName.tooltip=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. To prevent nesting and use dot literally, escape the dot with backslash (\\.).
jsonType.label=Claim JSON Type
jsonType.tooltip=JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values.
includeInIdToken.label=Add to ID token