Merge pull request #3569 from mposolda/master

KEYCLOAK-3831 Improve AddressMapper configurability. Support for 'for…
This commit is contained in:
Marek Posolda 2016-11-30 13:44:18 +01:00 committed by GitHub
commit 45abaec86e
3 changed files with 74 additions and 26 deletions

View file

@ -17,14 +17,11 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AddressClaimSet;
import org.keycloak.representations.IDToken;
@ -34,7 +31,6 @@ import java.util.List;
import java.util.Map;
/**
* Set the 'name' claim to be first + last name.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@ -43,26 +39,39 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
public static final String STREET = "street";
static {
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AddressMapper.class);
configProperties.add(createConfigProperty(STREET));
configProperties.add(createConfigProperty(AddressClaimSet.LOCALITY));
configProperties.add(createConfigProperty(AddressClaimSet.REGION));
configProperties.add(createConfigProperty(AddressClaimSet.POSTAL_CODE));
configProperties.add(createConfigProperty(AddressClaimSet.COUNTRY));
configProperties.add(createConfigProperty(AddressClaimSet.FORMATTED));
}
protected static ProviderConfigProperty createConfigProperty(String claimName) {
ProviderConfigProperty property = new ProviderConfigProperty();
property.setName(getModelPropertyName(claimName));
property.setLabel("addressClaim." + claimName + ".label");
property.setHelpText("addressClaim." + claimName + ".tooltip");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setDefaultValue(claimName);
return property;
}
public static String getModelPropertyName(String claimName) {
return "user.attribute." + claimName;
}
public static final String PROVIDER_ID = "oidc-address-mapper";
public static ProtocolMapperModel createAddressMapper() {
Map<String, String> config;
ProtocolMapperModel address = new ProtocolMapperModel();
address.setName("address");
address.setProtocolMapper(PROVIDER_ID);
address.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
address.setConsentRequired(true);
address.setConsentText("${address}");
config = new HashMap<String, String>();
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
address.setConfig(config);
return address;
return createAddressMapper(true, true);
}
public static ProtocolMapperModel createAddressMapper(boolean idToken, boolean accessToken) {
Map<String, String> config;
ProtocolMapperModel address = new ProtocolMapperModel();
@ -74,6 +83,14 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
config = new HashMap<String, String>();
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, Boolean.toString(idToken));
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, Boolean.toString(accessToken));
config.put(getModelPropertyName(STREET), STREET);
config.put(getModelPropertyName(AddressClaimSet.LOCALITY), AddressClaimSet.LOCALITY);
config.put(getModelPropertyName(AddressClaimSet.REGION), AddressClaimSet.REGION);
config.put(getModelPropertyName(AddressClaimSet.POSTAL_CODE), AddressClaimSet.POSTAL_CODE);
config.put(getModelPropertyName(AddressClaimSet.COUNTRY), AddressClaimSet.COUNTRY);
config.put(getModelPropertyName(AddressClaimSet.FORMATTED), AddressClaimSet.FORMATTED);
address.setConfig(config);
return address;
}
@ -107,12 +124,24 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
UserModel user = userSession.getUser();
AddressClaimSet addressSet = new AddressClaimSet();
addressSet.setStreetAddress(user.getFirstAttribute("street"));
addressSet.setLocality(user.getFirstAttribute("locality"));
addressSet.setRegion(user.getFirstAttribute("region"));
addressSet.setPostalCode(user.getFirstAttribute("postal_code"));
addressSet.setCountry(user.getFirstAttribute("country"));
addressSet.setStreetAddress(getUserModelAttributeValue(user, mappingModel, STREET));
addressSet.setLocality(getUserModelAttributeValue(user, mappingModel, AddressClaimSet.LOCALITY));
addressSet.setRegion(getUserModelAttributeValue(user, mappingModel, AddressClaimSet.REGION));
addressSet.setPostalCode(getUserModelAttributeValue(user, mappingModel, AddressClaimSet.POSTAL_CODE));
addressSet.setCountry(getUserModelAttributeValue(user, mappingModel, AddressClaimSet.COUNTRY));
addressSet.setFormattedAddress(getUserModelAttributeValue(user, mappingModel, AddressClaimSet.FORMATTED));
token.getOtherClaims().put("address", addressSet);
}
private String getUserModelAttributeValue(UserModel user, ProtocolMapperModel mappingModel, String claim) {
String modelPropertyName = getModelPropertyName(claim);
String userAttrName = mappingModel.getConfig().get(modelPropertyName);
if (userAttrName == null) {
userAttrName = claim;
}
return user.getFirstAttribute(userAttrName);
}
}

View file

@ -29,7 +29,9 @@ 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.protocol.oidc.mappers.AddressMapper;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AddressClaimSet;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@ -96,11 +98,13 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
user.singleAttribute("street", "5 Yawkey Way");
user.singleAttribute("locality", "Boston");
user.singleAttribute("region", "MA");
user.singleAttribute("region_some", "MA"); // Custom name for userAttribute name, which will be mapped to region
user.singleAttribute("postal_code", "02115");
user.singleAttribute("country", "USA");
user.singleAttribute("formatted", "6 Foo Street");
user.singleAttribute("phone", "617-777-6666");
List<String> departments = Arrays.asList("finance", "development");
user.getAttributes().put("departments", departments);
userResource.update(user);
@ -108,6 +112,9 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
ProtocolMapperRepresentation mapper = createAddressMapper(true, true);
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.REGION), "region_some");
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.COUNTRY), "country_some");
mapper.getConfig().remove(AddressMapper.getModelPropertyName(AddressClaimSet.POSTAL_CODE)); // Even if we remove protocolMapper config property, it should still default to postal_code
app.getProtocolMappers().createMapper(mapper);
ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", false, null, true, true);
@ -131,7 +138,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
assertEquals(idToken.getAddress().getLocality(), "Boston");
assertEquals(idToken.getAddress().getRegion(), "MA");
assertEquals(idToken.getAddress().getPostalCode(), "02115");
assertEquals(idToken.getAddress().getCountry(), "USA");
assertNull(idToken.getAddress().getCountry()); // Null because we changed userAttribute name to "country_some", but user contains "country"
assertEquals(idToken.getAddress().getFormattedAddress(), "6 Foo Street");
assertNotNull(idToken.getOtherClaims().get("home_phone"));
assertEquals("617-777-6666", idToken.getOtherClaims().get("home_phone"));
assertEquals("coded", idToken.getOtherClaims().get("hard"));
@ -150,7 +158,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
assertEquals(accessToken.getAddress().getLocality(), "Boston");
assertEquals(accessToken.getAddress().getRegion(), "MA");
assertEquals(accessToken.getAddress().getPostalCode(), "02115");
assertEquals(accessToken.getAddress().getCountry(), "USA");
assertNull(idToken.getAddress().getCountry()); // Null because we changed userAttribute name to "country_some", but user contains "country"
assertEquals(idToken.getAddress().getFormattedAddress(), "6 Foo Street");
assertNotNull(accessToken.getOtherClaims().get("home_phone"));
assertEquals("617-777-6666", accessToken.getOtherClaims().get("home_phone"));
assertEquals("coded", accessToken.getOtherClaims().get("hard"));

View file

@ -170,8 +170,18 @@ sectorIdentifierUri.label=Sector Identifier URI
sectorIdentifierUri.tooltip=Providers that use pairwise sub values and support Dynamic Client Registration SHOULD use the sector_identifier_uri parameter. It provides a way for a group of websites under common administrative control to have consistent pairwise sub values independent of the individual domain names. It also provides a way for Clients to change redirect_uri domains without having to reregister all of their users.
pairwiseSubAlgorithmSalt.label=Salt
pairwiseSubAlgorithmSalt.tooltip=Salt used when calculating the pairwise subject identifier. If left blank, a salt will be generated.
addressClaim.street.label=User Attribute Name for Street
addressClaim.street.tooltip=Name of User Attribute, which will be used to map to 'street_address' subclaim inside 'address' token claim. Defaults to 'street' .
addressClaim.locality.label=User Attribute Name for Locality
addressClaim.locality.tooltip=Name of User Attribute, which will be used to map to 'locality' subclaim inside 'address' token claim. Defaults to 'locality' .
addressClaim.region.label=User Attribute Name for Region
addressClaim.region.tooltip=Name of User Attribute, which will be used to map to 'region' subclaim inside 'address' token claim. Defaults to 'region' .
addressClaim.postal_code.label=User Attribute Name for Postal Code
addressClaim.postal_code.tooltip=Name of User Attribute, which will be used to map to 'postal_code' subclaim inside 'address' token claim. Defaults to 'postal_code' .
addressClaim.country.label=User Attribute Name for Country
addressClaim.country.tooltip=Name of User Attribute, which will be used to map to 'country' subclaim inside 'address' token claim. Defaults to 'country' .
addressClaim.formatted.label=User Attribute Name for Formatted Address
addressClaim.formatted.tooltip=Name of User Attribute, which will be used to map to 'formatted' subclaim inside 'address' token claim. Defaults to 'formatted' .
# client details
clients.tooltip=Clients are trusted browser apps and web services in a realm. These clients can request a login. You can also define client specific roles.