Merge pull request #2433 from patriot1burke/master

KEYCLOAK-2624
This commit is contained in:
Bill Burke 2016-03-24 16:52:29 -04:00
commit cb4f95416c
4 changed files with 440 additions and 8 deletions

View file

@ -53,7 +53,7 @@ public class UserAttributeMapper extends AbstractClaimMapper {
property = new ProviderConfigProperty();
property.setName(USER_ATTRIBUTE);
property.setLabel("User Attribute Name");
property.setHelpText("User attribute name to store claim.");
property.setHelpText("User attribute name to store claim. Use email, lastName, and firstName to map to those predefined user properties.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
@ -90,25 +90,43 @@ public class UserAttributeMapper extends AbstractClaimMapper {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
Object value = getClaimValue(mapperModel, context);
if (value != null) {
if (attribute.equalsIgnoreCase("email")) {
context.setEmail(value.toString());
} else if (attribute.equalsIgnoreCase("firstName")) {
context.setFirstName(value.toString());
} else if (attribute.equalsIgnoreCase("lastName")) {
context.setLastName(value.toString());
} else {
context.setUserAttribute(attribute, value.toString());
}
}
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
Object value = getClaimValue(mapperModel, context);
String stringValue = null;
if (value != null) stringValue = value.toString();
if (attribute.equalsIgnoreCase("email")) {
user.setEmail(stringValue);
} else if (attribute.equalsIgnoreCase("firstName")) {
user.setFirstName(stringValue);
} else if (attribute.equalsIgnoreCase("lastName")) {
user.setLastName(stringValue);
} else {
String current = user.getFirstAttribute(attribute);
if (value != null && !value.equals(current)) {
user.setSingleAttribute(attribute, value.toString());
if (stringValue != null && !stringValue.equals(current)) {
user.setSingleAttribute(attribute, stringValue);
} else if (value == null) {
user.removeAttribute(attribute);
}
}
}
@Override
public String getHelpText() {
return "Import declared claim if it exists in ID or access token into the specified user attribute.";
return "Import declared claim if it exists in ID or access token into the specified user property or attribute.";
}
}

View file

@ -0,0 +1,213 @@
/*
* 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.*;
/**
* Test that the broker AttributeMapper maps user properties like email, firstName, and lastName
* @author pedroigor
*/
public class OIDCBrokerUserPropertyTest 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-oidc-property-mappers.json"));
}
@Override
protected String[] getTestRealms() {
return new String[] { "realm-with-oidc-idp-property-mappers" };
}
};
@Override
protected String getProviderId() {
return "kc-oidc-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
}
}

View file

@ -0,0 +1,145 @@
{
"id": "realm-with-oidc-idp-property-mappers",
"realm": "realm-with-oidc-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": "broker-app",
"consentRequired": "false",
"enabled": true,
"fullScopeAllowed": true,
"secret": "secret",
"redirectUris": [
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-oidc-idp-property-mappers/endpoint/*"
],
"protocolMappers": [
{
"name": "mobile",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-attribute-mapper",
"consentRequired": false,
"config": {
"user.attribute": "mobile",
"claim.name": "mobile",
"Claim JSON Type": "String",
"access.token.claim": "true",
"id.token.claim": "true"
}
},
{
"name": "username",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"user.attribute": "username",
"claim.name": "preferred_username",
"Claim JSON Type": "String",
"access.token.claim": "true",
"id.token.claim": "true"
}
},
{
"name": "email",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"user.attribute": "email",
"claim.name": "email",
"Claim JSON Type": "String",
"access.token.claim": "true",
"id.token.claim": "true"
}
},
{
"name": "given name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"user.attribute": "firstName",
"claim.name": "given_name",
"Claim JSON Type": "String",
"access.token.claim": "true",
"id.token.claim": "true"
}
},
{
"name": "family name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"user.attribute": "lastName",
"claim.name": "family_name",
"Claim JSON Type": "String",
"access.token.claim": "true",
"id.token.claim": "true"
}
}
]
}
],
"scopeMappings": [
{
"client": "broker-app",
"roles": ["manager"]
}
],
"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"
}
]
}
}

View file

@ -186,9 +186,65 @@
"defaultScope": "email profile",
"backchannelSupported": "true"
}
},
{
"alias" : "kc-oidc-idp-property-mappers",
"providerId" : "keycloak-oidc",
"enabled": true,
"storeToken" : true,
"addReadTokenRoleOnCreate": true,
"config": {
"clientId": "broker-app",
"clientSecret": "secret",
"prompt": "login",
"authorizationUrl": "http://localhost:8082/auth/realms/realm-with-oidc-idp-property-mappers/protocol/openid-connect/auth",
"tokenUrl": "http://localhost:8082/auth/realms/realm-with-oidc-idp-property-mappers/protocol/openid-connect/token",
"userInfoUrl": "http://localhost:8082/auth/realms/realm-with-oidc-idp-property-mappers/protocol/openid-connect/userinfo",
"logoutUrl": "http://localhost:8082/auth/realms/realm-with-oidc-idp-property-mappers/protocol/openid-connect/logout",
"defaultScope": "email profile",
"backchannelSupported": "true"
}
}
],
"identityProviderMappers": [
{
"name": "manager-mapper",
"identityProviderAlias": "kc-oidc-idp-property-mappers",
"identityProviderMapper": "oidc-role-idp-mapper",
"config": {
"role": "manager",
"claim": "realm_access.roles",
"claim.value": "manager"
}
},
{
"name": "email-mapper",
"identityProviderAlias": "kc-oidc-idp-property-mappers",
"identityProviderMapper": "oidc-user-attribute-idp-mapper",
"config": {
"user.attribute": "email",
"claim": "email"
}
},
{
"name": "first-mapper",
"identityProviderAlias": "kc-oidc-idp-property-mappers",
"identityProviderMapper": "oidc-user-attribute-idp-mapper",
"config": {
"user.attribute": "firstName",
"claim": "given_name"
}
},
{
"name": "last-mapper",
"identityProviderAlias": "kc-oidc-idp-property-mappers",
"identityProviderMapper": "oidc-user-attribute-idp-mapper",
"config": {
"user.attribute": "lastName",
"claim": "family_name"
}
},
{
"name": "manager-mapper",
"identityProviderAlias": "kc-oidc-idp",