keycloak-aplcache
Changes
services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLBrokerUserPropertyTest.java 212(+212 -0)
Details
diff --git a/services/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java b/services/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java
index 0a24e29..70cae07 100755
--- a/services/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java
+++ b/services/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java
@@ -64,7 +64,7 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
property = new ProviderConfigProperty();
property.setName(USER_ATTRIBUTE);
property.setLabel("User Attribute Name");
- property.setHelpText("User attribute name to store saml attribute.");
+ property.setHelpText("User attribute name to store saml attribute. Use email, lastName, and firstName to map to those predefined user properties.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
@@ -99,9 +99,17 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
@Override
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
- Object value = getAttribute(mapperModel, context);
+ String value = getAttribute(mapperModel, context);
if (value != null) {
- context.setUserAttribute(attribute, value.toString());
+ if (attribute.equalsIgnoreCase("email")) {
+ context.setEmail(value);
+ } else if (attribute.equalsIgnoreCase("firstName")) {
+ context.setFirstName(value);
+ } else if (attribute.equalsIgnoreCase("lastName")) {
+ context.setLastName(value);
+ } else {
+ context.setUserAttribute(attribute, value);
+ }
}
}
@@ -128,19 +136,29 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
- Object value = getAttribute(mapperModel, context);
- String current = user.getFirstAttribute(attribute);
- if (value != null && !value.equals(current)) {
- user.setSingleAttribute(attribute, value.toString());
- } else if (value == null) {
- user.removeAttribute(attribute);
+ String value = getAttribute(mapperModel, context);
+ if (attribute.equalsIgnoreCase("email")) {
+ user.setEmail(value);
+ } else if (attribute.equalsIgnoreCase("firstName")) {
+ user.setFirstName(value);
+ } else if (attribute.equalsIgnoreCase("lastName")) {
+ user.setLastName(value);
+ } else {
+ String current = user.getFirstAttribute(attribute);
+ if (value != null && !value.equals(current)) {
+ user.setSingleAttribute(attribute, value.toString());
+ } else if (value == null) {
+ user.removeAttribute(attribute);
+ }
}
+
+
}
@Override
public String getHelpText() {
- return "Import declared saml attribute if it exists in assertion into the specified user attribute.";
+ return "Import declared saml attribute if it exists in assertion into the specified user property or attribute.";
}
}
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 e56a2be..7897d2b 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
@@ -76,7 +76,7 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI
@Override
public String getHelpText() {
- return "Map a built in user property to a token claim.";
+ return "Map a built in user property (email, firstName, lastName) to a token claim.";
}
@Override
diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java
index 0550bab..55eee20 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java
@@ -72,7 +72,7 @@ public class UserPropertyAttributeStatementMapper extends AbstractSAMLProtocolMa
@Override
public String getHelpText() {
- return "Map a built in user property to a SAML attribute type.";
+ return "Map a built in user property (email, firstName, lastName) to a SAML attribute type.";
}
@Override
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLBrokerUserPropertyTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLBrokerUserPropertyTest.java
new file mode 100755
index 0000000..037b808
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLBrokerUserPropertyTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.broker;
+
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
+import org.keycloak.saml.processing.web.util.PostBindingUtil;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.KeycloakServer;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+
+import javax.mail.MessagingException;
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author pedroigor
+ */
+public class SAMLBrokerUserPropertyTest extends AbstractKeycloakIdentityProviderTest {
+
+ @ClassRule
+ public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
+
+ @Override
+ protected void configureServer(KeycloakServer server) {
+ server.getConfig().setPort(8082);
+ }
+
+ @Override
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+ server.importRealm(getClass().getResourceAsStream("/broker-test/realm-with-saml-property-mappers.json"));
+ }
+
+ @Override
+ protected String[] getTestRealms() {
+ return new String[] { "realm-with-saml-idp-property-mappers" };
+ }
+ };
+
+ @Override
+ protected String getProviderId() {
+ return "kc-saml-idp-property-mappers";
+ }
+
+ @Override
+ protected void doAssertFederatedUser(UserModel federatedUser, IdentityProviderModel identityProviderModel, String expectedEmail, boolean isProfileUpdateExpected) {
+ if (isProfileUpdateExpected) {
+ super.doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail, isProfileUpdateExpected);
+ } else {
+ assertEquals(expectedEmail, federatedUser.getEmail());
+ assertNotNull(federatedUser.getFirstName());
+ assertNotNull(federatedUser.getLastName());
+ }
+ }
+
+ @Override
+ protected void doAssertFederatedUserNoEmail(UserModel federatedUser) {
+ assertEquals("kc-saml-idp-basic.test-user-noemail", federatedUser.getUsername());
+ //assertEquals("", federatedUser.getEmail());
+ assertEquals(null, federatedUser.getFirstName());
+ assertEquals(null, federatedUser.getLastName());
+ }
+
+ @Override
+ protected void doAssertTokenRetrieval(String pageSource) {
+ try {
+ SAML2Request saml2Request = new SAML2Request();
+ ResponseType responseType = (ResponseType) saml2Request
+ .getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(pageSource));
+ //.getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(pageSource, "UTF-8")));
+
+ assertNotNull(responseType);
+ assertFalse(responseType.getAssertions().isEmpty());
+ } catch (Exception e) {
+ fail("Could not parse token.");
+ }
+ }
+
+ @Override
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile() {
+ super.testSuccessfulAuthenticationWithoutUpdateProfile();
+ }
+
+ @Test
+ @Ignore
+ @Override
+ public void testSuccessfulAuthentication() {
+ // ignore
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void testSuccessfulAuthenticationUpdateProfileOnMissing_nothingMissing() {
+ // ignore
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void testSuccessfulAuthenticationUpdateProfileOnMissing_missingEmail() {
+ // ignore
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled() throws IOException, MessagingException {
+ // ignore
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() {
+ // ignore
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled_emailTrustEnabled() {
+ // ignore
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void testSuccessfulAuthentication_emailTrustEnabled_emailVerifyEnabled_emailUpdatedOnFirstLogin() throws IOException, MessagingException {
+ // ignore
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername() {
+ // ignore
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void testDisabled() {
+ // ignore
+ }
+
+ @Override
+ @Test
+ @Ignore
+ public void testProviderOnLoginPage() {
+ // ignore
+ }
+
+ @Override
+ @Test
+ @Ignore
+ public void testAccountManagementLinkIdentity() {
+ // ignore
+ }
+
+ @Override
+ @Test
+ @Ignore
+ public void testAccountManagementLinkedIdentityAlreadyExists() {
+ // ignore
+ }
+
+ @Override
+ @Test
+ @Ignore
+ public void testIdentityProviderNotAllowed() {
+ // ignore
+ }
+
+ @Override
+ @Test
+ @Ignore
+ public void testTokenStorageAndRetrievalByApplication() {
+ // ignore
+ }
+
+ @Override
+ @Test
+ @Ignore
+ public void testWithLinkedFederationProvider() throws Exception {
+ // ignore
+ }
+}
diff --git a/testsuite/integration/src/test/resources/broker-test/realm-with-saml-property-mappers.json b/testsuite/integration/src/test/resources/broker-test/realm-with-saml-property-mappers.json
new file mode 100755
index 0000000..3703c7b
--- /dev/null
+++ b/testsuite/integration/src/test/resources/broker-test/realm-with-saml-property-mappers.json
@@ -0,0 +1,124 @@
+{
+ "id": "realm-with-saml-idp-property-mappers",
+ "realm": "realm-with-saml-idp-property-mappers",
+ "enabled": true,
+ "requiredCredentials": [ "password" ],
+ "defaultRoles": [ "foo", "bar" ],
+ "privateKey": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCCPyvTTb14vSMkpe/pds2P5Cqxk7bkeFnQiNMS1vyZ+HS2O79fxzp1eAguHnBTs4XTRT7SZJhIT/6utgqZjmDigKV5N7X5ptq8BM/W1qa1cYBRip261pc+tWf3IywJYQ9yFI9mUQarmIEl0D7GH16NSZklheaWfbodRVarvX+ML0amNtGYVDft/RftYmgbKKrK218qQp9R4GZFtf/Q/RmboNXN7weMINU8GWVkTRrccKBIXSunT6zXGfuj3Wp1YpVq20BWwY2OMM/P+yDAc7LKEO1LJqPBdT4r9BRn2lXiaga3AL24gTKZPKU/tu7uqfFciF+i4Rr58SMDNOzQcnklAgMBAAECggEAc0eibJYEO5d8QXW1kPgcHV2gBChv2mxDYnWYDLbIQSdNdfYP/qABt/MTmm5KkWr16fcCEYoD1w0mqFBrtVn1msSusUmEAYGTXJMNumOmjjX1kzaTQMmqeFBrwqwYz/xehWR5P+A7fSmwNV3KEeW19GvN5w5K96w0TLAQdFV3TQVPSytusDunwuR1yltMe1voaEDZ9z0Pi08YiEk2f6xhj5CMkoiw3mNImzfruphHullxU4FD05fH6tDeJ381527ILpAzDsgYZh4aFLKjUHem96bX4EL7FIzBJ6okgN78AZnUC/EaVfgFTw0qfhoWvZV4ruVXXiMhCg4CMMRDq/k9iQKBgQDBNWsJMT84OnnWmQoJmZogkFV+tsGrSK6Re+aJxLWpishh7dwAnT2OcagZvVdUb0FwNWu1D0B9/SKDDMRnnHBhOGDpH57m/eQdRU0oX1BD27xvffk0lLcfD4BTxnR5e9jss8K4twc9jf0P1rxC/loGJ2NtCH0BrPHgz54Ea+96ewKBgQCsk3JDaaPnFwzVYm2BXlhxOxLPsF4wvD2rIRAswZV4C5xebjand8nwiMmVpNd0PRLkEnkI+waURGv2EY/P3JsssoiY8Xqe8f/1G+SQKre7lbqOas8rFoALepC0BYDiZDFy0Z9ZnRAFzRI5sgIt7jpoMRD4xDNlmiV8X+yBxc3Y3wKBgQChDQsU1YUyNKQ8+sLAL9anEEkD4Ald4q8JPHN2IY+gLLxNzT0XEfsu0pTiJ8805axxgUYv3e/PVYNAJBNPnrqaf6lgiegl+jr9Hzhqz9CTUAYqFaL2boSakoxQyNtsLI0s+cb1vDN/3uy0GDZDzcty18BsMagqDmRtFgNNAj/UIwKBgQCahbeFBv0cOPZjxisY8Bou4N8aGehsqNBq/0LVYExuXa8YmoTTdJ3bgw9Er4G/ccQNdUDsuqAMeCtW/CiRzQ0ge4d1sprB4Rv3I4+HSsiS7SFKzfZLtWzXWlpg5qCdlWr1TR7qhYjIOPO9t1beO3YOvwhcRoliyyAPenBxTmTfbwKBgDtm2WJ5VlQgNpIdOs1CCiqd0DFmWOmvBPspPC1kySiy+Ndr9jNohRZkR7pEjgqA5E8rdzc88LirUN7bY5HFHRWN9KXrs5/o3O1K3GFCp64N6nvnPEYZ2zSJalcMC2fjSsJg26z8Dg1H+gfTIDUMoGiEAAnJXuqk+WayPU+fZMLn",
+ "publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgj8r0029eL0jJKXv6XbNj+QqsZO25HhZ0IjTEtb8mfh0tju/X8c6dXgILh5wU7OF00U+0mSYSE/+rrYKmY5g4oCleTe1+abavATP1tamtXGAUYqdutaXPrVn9yMsCWEPchSPZlEGq5iBJdA+xh9ejUmZJYXmln26HUVWq71/jC9GpjbRmFQ37f0X7WJoGyiqyttfKkKfUeBmRbX/0P0Zm6DVze8HjCDVPBllZE0a3HCgSF0rp0+s1xn7o91qdWKVattAVsGNjjDPz/sgwHOyyhDtSyajwXU+K/QUZ9pV4moGtwC9uIEymTylP7bu7qnxXIhfouEa+fEjAzTs0HJ5JQIDAQAB",
+ "clients": [
+ {
+ "clientId": "http://localhost:8081/auth/realms/realm-with-broker",
+ "enabled": true,
+ "protocol": "saml",
+ "redirectUris": [
+ "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-property-mappers/endpoint"
+ ],
+ "attributes": {
+ "saml.authnstatement": "true",
+ "saml_single_logout_service_url_post": "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-property-mappers/endpoint",
+ "saml_assertion_consumer_url_post": "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-property-mappers/endpoint",
+ "saml_force_name_id_format": "true",
+ "saml_name_id_format": "username"
+
+ },
+ "protocolMappers": [
+ {
+ "name": "role list",
+ "protocol": "saml",
+ "protocolMapper": "saml-role-list-mapper",
+ "consentRequired": false,
+ "config": {
+ "attribute.name": "Role",
+ "attribute.nameformat": "Basic",
+ "single": "false"
+
+ }
+ },
+ {
+ "name": "email",
+ "protocol": "saml",
+ "protocolMapper": "saml-user-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute": "email",
+ "attribute.name": "email",
+ "attribute.nameformat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
+ }
+ },
+ {
+ "name": "firstName",
+ "protocol": "saml",
+ "protocolMapper": "saml-user-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute": "firstName",
+ "attribute.name": "firstName",
+ "attribute.nameformat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
+ "friendly.name": "firstName"
+ }
+ },
+ {
+ "name": "lastName",
+ "protocol": "saml",
+ "protocolMapper": "saml-user-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute": "lastName",
+ "attribute.name": "lastName",
+ "attribute.nameformat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
+ "friendly.name": "lastName"
+ }
+ }
+
+ ]
+
+ }
+ ],
+ "users": [
+ {
+ "username" : "test-user",
+ "enabled": true,
+ "email" : "test-user@localhost",
+ "firstName" : "Test",
+ "lastName" : "User",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": ["manager"],
+ "attributes": {
+ "mobile": "617-666-7777"
+ }
+ },
+ {
+ "username" : "test-user-noemail",
+ "enabled": true,
+ "firstName" : "Test",
+ "lastName" : "User",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": ["manager"]
+ },
+ {
+ "username" : "pedroigor",
+ "enabled": true,
+ "email" : "psilva@redhat.com",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": ["manager"]
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "manager",
+ "description": "Have Manager privileges"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
index 28cc044..03ddbbe 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
@@ -140,6 +140,21 @@
}
},
{
+ "alias" : "kc-saml-idp-property-mappers",
+ "providerId" : "saml",
+ "enabled": true,
+ "trustEmail" : false,
+ "addReadTokenRoleOnCreate": true,
+ "config": {
+ "singleSignOnServiceUrl": "http://localhost:8082/auth/realms/realm-with-saml-idp-property-mappers/protocol/saml",
+ "singleLogoutServiceUrl": "http://localhost:8082/auth/realms/realm-with-saml-idp-property-mappers/protocol/saml",
+ "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
+ "forceAuthn": true,
+ "postBindingResponse": true,
+ "postBindingAuthnRequest": true
+ }
+ },
+ {
"alias" : "model-oidc-idp",
"providerId" : "oidc",
"enabled": false,
@@ -233,7 +248,46 @@
"user.attribute": "mobile",
"attribute.name": "mobile"
}
+ },
+ {
+ "name": "email-mapper",
+ "identityProviderAlias": "kc-saml-idp-property-mappers",
+ "identityProviderMapper": "saml-user-attribute-idp-mapper",
+ "config": {
+ "user.attribute": "email",
+ "attribute.name": "email"
+ }
+ },
+ {
+ "name": "firstName-mapper",
+ "identityProviderAlias": "kc-saml-idp-property-mappers",
+ "identityProviderMapper": "saml-user-attribute-idp-mapper",
+ "config": {
+ "user.attribute": "firstName",
+ "attribute.name": "firstName"
+ }
+ },
+ {
+ "name": "lastName-mapper",
+ "identityProviderAlias": "kc-saml-idp-property-mappers",
+ "identityProviderMapper": "saml-user-attribute-idp-mapper",
+ "config": {
+ "user.attribute": "lastName",
+ "attribute.name": "lastName"
+ }
+ },
+ {
+ "name": "manager-mapper",
+ "identityProviderAlias": "kc-saml-idp-property-mappers",
+ "identityProviderMapper": "saml-role-idp-mapper",
+ "config": {
+ "role": "manager",
+ "attribute.name": "Role",
+ "attribute.value": "manager"
+ }
+
}
+
],
"users": [
{