Merge pull request #3569 from mposolda/master
KEYCLOAK-3831 Improve AddressMapper configurability. Support for 'for…
This commit is contained in:
commit
45abaec86e
3 changed files with 74 additions and 26 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue