saml attribute mappers

This commit is contained in:
Bill Burke 2015-02-28 11:17:06 -05:00
parent 9dd9c9b37f
commit 2da925505e
18 changed files with 460 additions and 218 deletions

View file

@ -46,14 +46,8 @@ public class SALM2LoginResponseBuilder {
protected String requestID; protected String requestID;
protected String authMethod; protected String authMethod;
protected String requestIssuer; protected String requestIssuer;
protected Map<String, Object> attributes = new HashMap<String, Object>();
public SALM2LoginResponseBuilder attributes(Map<String, Object> attributes) {
this.attributes = attributes;
return this;
}
public SALM2LoginResponseBuilder destination(String destination) { public SALM2LoginResponseBuilder destination(String destination) {
this.destination = destination; this.destination = destination;
return this; return this;
@ -64,15 +58,6 @@ public class SALM2LoginResponseBuilder {
return this; return this;
} }
public SALM2LoginResponseBuilder attribute(String name, Object value) {
if (value == null) {
attributes.remove(name);
} else {
this.attributes.put(name, value);
}
return this;
}
public SALM2LoginResponseBuilder requestID(String requestID) { public SALM2LoginResponseBuilder requestID(String requestID) {
this.requestID =requestID; this.requestID =requestID;
return this; return this;
@ -176,11 +161,6 @@ public class SALM2LoginResponseBuilder {
assertion.addStatement(attrStatement); assertion.addStatement(attrStatement);
} }
// Add in the attributes information
if (attributes != null && attributes.size() > 0) {
AttributeStatementType attStatement = StatementUtil.createAttributeStatement(attributes);
assertion.addStatement(attStatement);
}
return responseType; return responseType;
} }

View file

@ -17,6 +17,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.ProtocolMapper; import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.saml.mappers.SAMLAttributeStatementMapper;
import org.keycloak.protocol.saml.mappers.SAMLLoginResponseMapper; import org.keycloak.protocol.saml.mappers.SAMLLoginResponseMapper;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.managers.ResourceAdminManager;
@ -29,6 +30,8 @@ import org.picketlink.common.exceptions.ConfigurationException;
import org.picketlink.common.exceptions.ParsingException; import org.picketlink.common.exceptions.ParsingException;
import org.picketlink.common.exceptions.ProcessingException; import org.picketlink.common.exceptions.ProcessingException;
import org.picketlink.identity.federation.core.saml.v2.constants.X500SAMLProfileConstants; import org.picketlink.identity.federation.core.saml.v2.constants.X500SAMLProfileConstants;
import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
import org.picketlink.identity.federation.saml.v2.protocol.ResponseType; import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
import org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler; import org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler;
import org.w3c.dom.Document; import org.w3c.dom.Document;
@ -273,6 +276,7 @@ public class SamlProtocol implements LoginProtocol {
Document samlDocument = null; Document samlDocument = null;
try { try {
ResponseType samlModel = builder.buildModel(); ResponseType samlModel = builder.buildModel();
transformAttributeStatement(session, samlModel, client, userSession, clientSession);
samlModel = transformLoginResponse(session, samlModel, client, userSession, clientSession); samlModel = transformLoginResponse(session, samlModel, client, userSession, clientSession);
samlDocument = builder.buildDocument(samlModel); samlDocument = builder.buildDocument(samlModel);
} catch (Exception e) { } catch (Exception e) {
@ -346,14 +350,14 @@ public class SamlProtocol implements LoginProtocol {
public void initClaims(SALM2LoginResponseBuilder builder, ClientModel model, UserModel user) { public void initClaims(SALM2LoginResponseBuilder builder, ClientModel model, UserModel user) {
if (ClaimMask.hasEmail(model.getAllowedClaimsMask())) { if (ClaimMask.hasEmail(model.getAllowedClaimsMask())) {
builder.attribute(X500SAMLProfileConstants.EMAIL_ADDRESS.getFriendlyName(), user.getEmail()); //builder.attribute(X500SAMLProfileConstants.EMAIL_ADDRESS.getFriendlyName(), user.getEmail());
} }
if (ClaimMask.hasName(model.getAllowedClaimsMask())) { if (ClaimMask.hasName(model.getAllowedClaimsMask())) {
builder.attribute(X500SAMLProfileConstants.GIVEN_NAME.getFriendlyName(), user.getFirstName()); //builder.attribute(X500SAMLProfileConstants.GIVEN_NAME.getFriendlyName(), user.getFirstName());
builder.attribute(X500SAMLProfileConstants.SURNAME.getFriendlyName(), user.getLastName()); //builder.attribute(X500SAMLProfileConstants.SURNAME.getFriendlyName(), user.getLastName());
} }
if (ClaimMask.hasUsername(model.getAllowedClaimsMask())) { if (ClaimMask.hasUsername(model.getAllowedClaimsMask())) {
builder.attribute(X500SAMLProfileConstants.USERID.getFriendlyName(), user.getUsername()); //builder.attribute(X500SAMLProfileConstants.USERID.getFriendlyName(), user.getUsername());
} }
} }
@ -373,6 +377,21 @@ public class SamlProtocol implements LoginProtocol {
} }
return response; return response;
} }
public void transformAttributeStatement(KeycloakSession session, ResponseType response, ClientModel client,
UserSessionModel userSession, ClientSessionModel clientSession) {
AttributeStatementType attributeStatement = new AttributeStatementType();
AssertionType assertion = response.getAssertions().get(0).getAssertion();
assertion.addStatement(attributeStatement);
Set<ProtocolMapperModel> mappings = client.getProtocolMappers();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
if (!mapping.getProtocol().equals(SamlProtocol.LOGIN_PROTOCOL)) continue;
ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
if (mapper == null || !(mapper instanceof SAMLAttributeStatementMapper)) continue;
((SAMLAttributeStatementMapper)mapper).transformAttributeStatement(attributeStatement, mapping, session, userSession, clientSession);
}
}

View file

@ -0,0 +1,72 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.protocol.ProtocolMapper;
import org.picketlink.common.constants.JBossSAMLURIConstants;
import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
import org.picketlink.identity.federation.saml.v2.assertion.AttributeType;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AttributeStatementHelper {
public static final String ATTRIBUTE_STATEMENT_CATEGORY = "AttributeStatement Mapper";
public static final String URI_REFERENCE = "URI Reference";
public static final String URI_REFERENCE_HELP_TEXT = "Attribute name for the SAML URI Reference attribute name format";
public static final String BASIC = "Basic name";
public static final String BASIC_HELP_TEXT = "Attribute name for the SAML Basic attribute name format";
public static final String FRIENDLY_NAME = "Friendly Name";
public static final String FRIENDLY_NAME_HELP_TEXT = "Standard SAML attribute setting. An optional, more human-readable form of the attribute's name that can be provided if the actual attribute name is cryptic.";
public static void addUriReferenceAttribute(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, String attributeValue) {
String attributeName = mappingModel.getConfig().get(URI_REFERENCE);
AttributeType attribute = new AttributeType(attributeName);
attribute.setNameFormat(JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get());
String friendlyName = mappingModel.getConfig().get(FRIENDLY_NAME);
if (friendlyName != null && friendlyName.trim().equals("")) friendlyName = null;
if (friendlyName != null) attribute.setFriendlyName(friendlyName);
attribute.addAttributeValue(attributeValue);
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attribute));
}
public static void addBasicAttribute(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, String attributeValue) {
String attributeName = mappingModel.getConfig().get(BASIC);
AttributeType attribute = new AttributeType(attributeName);
attribute.setNameFormat(JBossSAMLURIConstants.ATTRIBUTE_FORMAT_BASIC.get());
String friendlyName = mappingModel.getConfig().get(FRIENDLY_NAME);
if (friendlyName != null && friendlyName.trim().equals("")) friendlyName = null;
if (friendlyName != null) attribute.setFriendlyName(friendlyName);
attribute.addAttributeValue(attributeValue);
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attribute));
}
protected static void addUriReferenceProperties(List<ProtocolMapper.ConfigProperty> configProperties) {
ProtocolMapper.ConfigProperty property;
property = new ProtocolMapper.ConfigProperty();
property.setName(FRIENDLY_NAME);
property.setLabel(FRIENDLY_NAME);
property.setHelpText(FRIENDLY_NAME_HELP_TEXT);
configProperties.add(property);
property = new ProtocolMapper.ConfigProperty();
property.setName(URI_REFERENCE);
property.setLabel(URI_REFERENCE);
property.setHelpText(URI_REFERENCE_HELP_TEXT);
configProperties.add(property);
}
protected static void addBasicProperties(List<ProtocolMapper.ConfigProperty> configProperties) {
ProtocolMapper.ConfigProperty property;
property = new ProtocolMapper.ConfigProperty();
property.setName(FRIENDLY_NAME);
property.setLabel(FRIENDLY_NAME);
property.setHelpText(FRIENDLY_NAME_HELP_TEXT);
configProperties.add(property);
property = new ProtocolMapper.ConfigProperty();
property.setName(BASIC);
property.setLabel(BASIC);
property.setHelpText(BASIC_HELP_TEXT);
configProperties.add(property);
}
}

View file

@ -0,0 +1,18 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface SAMLAttributeStatementMapper {
void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession);
}

View file

@ -0,0 +1,69 @@
package org.keycloak.protocol.saml.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.ProtocolMapperUtils;
import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
import java.util.ArrayList;
import java.util.List;
/**
* Mappings UserModel property (the property name of a getter method) to an AttributeStatement.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserAttributeBasicAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
static {
ConfigProperty property;
property = new ConfigProperty();
property.setName(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_NAME);
property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_NAME);
property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
configProperties.add(property);
AttributeStatementHelper.addBasicProperties(configProperties);
}
public static final String PROVIDER_ID = "saml-user-attribute-basic-mapper";
public List<ConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String getDisplayType() {
return "User Attribute Basic";
}
@Override
public String getDisplayCategory() {
return AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY;
}
@Override
public String getHelpText() {
return "Map a custom user attribute to a to a SAML Basic attribute type..";
}
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
UserModel user = userSession.getUser();
String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_NAME);
String attributeValue = user.getAttribute(attributeName);
AttributeStatementHelper.addBasicAttribute(attributeStatement, mappingModel, attributeValue);
}
}

View file

@ -0,0 +1,69 @@
package org.keycloak.protocol.saml.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.ProtocolMapperUtils;
import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
import java.util.ArrayList;
import java.util.List;
/**
* Mappings UserModel property (the property name of a getter method) to an AttributeStatement.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserAttributeUriReferenceAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
static {
ConfigProperty property;
property = new ConfigProperty();
property.setName(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_NAME);
property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_NAME);
property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
configProperties.add(property);
AttributeStatementHelper.addUriReferenceProperties(configProperties);
}
public static final String PROVIDER_ID = "saml-user-attribute-uri-mapper";
public List<ConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String getDisplayType() {
return "User Attribute URI";
}
@Override
public String getDisplayCategory() {
return AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY;
}
@Override
public String getHelpText() {
return "Map a custom user attribute to a to a SAML URI reference attribute type..";
}
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
UserModel user = userSession.getUser();
String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_NAME);
String attributeValue = user.getAttribute(attributeName);
AttributeStatementHelper.addUriReferenceAttribute(attributeStatement, mappingModel, attributeValue);
}
}

View file

@ -0,0 +1,69 @@
package org.keycloak.protocol.saml.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.ProtocolMapperUtils;
import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
import java.util.ArrayList;
import java.util.List;
/**
* Mappings UserModel property (the property name of a getter method) to an AttributeStatement.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserModelBasicAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
static {
ConfigProperty property;
property = new ConfigProperty();
property.setName(ProtocolMapperUtils.USER_MODEL_PROPERTY);
property.setLabel(ProtocolMapperUtils.USER_MODEL_PROPERTY);
property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT);
configProperties.add(property);
AttributeStatementHelper.addBasicProperties(configProperties);
}
public static final String PROVIDER_ID = "saml-user-property-base-mapper";
public List<ConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String getDisplayType() {
return "User Property Basic";
}
@Override
public String getDisplayCategory() {
return AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY;
}
@Override
public String getHelpText() {
return "Map a built in user property to a SAML Basic attribute type.";
}
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
UserModel user = userSession.getUser();
String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_PROPERTY);
String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName);
AttributeStatementHelper.addBasicAttribute(attributeStatement, mappingModel, propertyValue);
}
}

View file

@ -0,0 +1,69 @@
package org.keycloak.protocol.saml.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.ProtocolMapperUtils;
import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
import java.util.ArrayList;
import java.util.List;
/**
* Mappings UserModel property (the property name of a getter method) to an AttributeStatement.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserModelUriReferenceAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
static {
ConfigProperty property;
property = new ConfigProperty();
property.setName(ProtocolMapperUtils.USER_MODEL_PROPERTY);
property.setLabel(ProtocolMapperUtils.USER_MODEL_PROPERTY);
property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT);
configProperties.add(property);
AttributeStatementHelper.addUriReferenceProperties(configProperties);
}
public static final String PROVIDER_ID = "saml-user-property-uri-mapper";
public List<ConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String getDisplayType() {
return "User Property URI";
}
@Override
public String getDisplayCategory() {
return AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY;
}
@Override
public String getHelpText() {
return "Map a built in user property to a SAML URI reference attribute type.";
}
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
UserModel user = userSession.getUser();
String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_PROPERTY);
String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName);
AttributeStatementHelper.addUriReferenceAttribute(attributeStatement, mappingModel, propertyValue);
}
}

View file

@ -0,0 +1,6 @@
org.keycloak.protocol.saml.mappers.UserAttributeBasicAttributeStatementMapper
org.keycloak.protocol.saml.mappers.UserModelBasicAttributeStatementMapper
org.keycloak.protocol.saml.mappers.UserAttributeUriReferenceAttributeStatementMapper
org.keycloak.protocol.saml.mappers.UserModelUriReferenceAttributeStatementMapper

View file

@ -0,0 +1,37 @@
package org.keycloak.protocol;
import org.keycloak.models.UserModel;
import java.lang.reflect.Method;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ProtocolMapperUtils {
public static final String USER_MODEL_PROPERTY = "User Property";
public static final String USER_MODEL_PROPERTY_HELP_TEXT = "Name of the property method in the UserModel interface. For example, a value of 'email' would reference the UserModel.getEmail() method.";
public static final String USER_MODEL_ATTRIBUTE_NAME = "User Attribute";
public static final String USER_MODEL_ATTRIBUTE_HELP_TEXT = "Name of stored user attribute which is the name of an attribute within the UserModel.attribute map.";
public static String getUserModelValue(UserModel user, String propertyName) {
String methodName = "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
try {
Method method = UserModel.class.getMethod(methodName);
Object val = method.invoke(user);
if (val != null) return val.toString();
} catch (Exception ignore) {
}
methodName = "is" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
try {
Method method = UserModel.class.getMethod(methodName);
Object val = method.invoke(user);
if (val != null) return val.toString();
} catch (Exception ignore) {
}
return null;
}
}

View file

@ -8,7 +8,8 @@ import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.LoginProtocolFactory; import org.keycloak.protocol.LoginProtocolFactory;
import org.keycloak.protocol.oidc.mappers.AttributeMapperHelper; import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.oidc.mappers.OIDCAddressMapper; import org.keycloak.protocol.oidc.mappers.OIDCAddressMapper;
import org.keycloak.protocol.oidc.mappers.OIDCFullNameMapper; import org.keycloak.protocol.oidc.mappers.OIDCFullNameMapper;
import org.keycloak.protocol.oidc.mappers.OIDCUserModelMapper; import org.keycloak.protocol.oidc.mappers.OIDCUserModelMapper;
@ -65,27 +66,27 @@ public class OIDCLoginProtocolFactory implements LoginProtocolFactory {
int counter = 0; int counter = 0;
// the ids must never change!!!! So if you add more default mappers, then add to end with higher counter. // the ids must never change!!!! So if you add more default mappers, then add to end with higher counter.
addClaimMapper(realm, "username", OIDCUserModelMapper.PROVIDER_ID, addClaimMapper(realm, "username", OIDCUserModelMapper.PROVIDER_ID,
OIDCUserModelMapper.USER_MODEL_PROPERTY, "username", ProtocolMapperUtils.USER_MODEL_PROPERTY, "username",
"preferred_username", "String", "preferred_username", "String",
true, "username", true, "username",
true); true);
addClaimMapper(realm, "email", OIDCUserModelMapper.PROVIDER_ID, addClaimMapper(realm, "email", OIDCUserModelMapper.PROVIDER_ID,
OIDCUserModelMapper.USER_MODEL_PROPERTY, "email", ProtocolMapperUtils.USER_MODEL_PROPERTY, "email",
"email", "String", "email", "String",
true, "email", true, "email",
true); true);
addClaimMapper(realm, "given name", OIDCUserModelMapper.PROVIDER_ID, addClaimMapper(realm, "given name", OIDCUserModelMapper.PROVIDER_ID,
OIDCUserModelMapper.USER_MODEL_PROPERTY, "firstName", ProtocolMapperUtils.USER_MODEL_PROPERTY, "firstName",
"given_name", "String", "given_name", "String",
true, "given name", true, "given name",
true); true);
addClaimMapper(realm, "family name", OIDCUserModelMapper.PROVIDER_ID, addClaimMapper(realm, "family name", OIDCUserModelMapper.PROVIDER_ID,
OIDCUserModelMapper.USER_MODEL_PROPERTY, "lastName", ProtocolMapperUtils.USER_MODEL_PROPERTY, "lastName",
"family_name", "String", "family_name", "String",
true, "family name", true, "family name",
true); true);
addClaimMapper(realm, "email verified", OIDCUserModelMapper.PROVIDER_ID, addClaimMapper(realm, "email verified", OIDCUserModelMapper.PROVIDER_ID,
OIDCUserModelMapper.USER_MODEL_PROPERTY, "emailVerified", ProtocolMapperUtils.USER_MODEL_PROPERTY, "emailVerified",
"email_verified", "boolean", "email_verified", "boolean",
false, null, false, null,
false); false);
@ -131,8 +132,8 @@ public class OIDCLoginProtocolFactory implements LoginProtocolFactory {
mapper.setAppliedByDefault(appliedByDefault); mapper.setAppliedByDefault(appliedByDefault);
Map<String, String> config = new HashMap<String, String>(); Map<String, String> config = new HashMap<String, String>();
config.put(propertyName, propertyNameValue); config.put(propertyName, propertyNameValue);
config.put(AttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName); config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
config.put(AttributeMapperHelper.JSON_TYPE, claimType); config.put(OIDCAttributeMapperHelper.JSON_TYPE, claimType);
mapper.setConfig(config); mapper.setConfig(config);
realm.addProtocolMapper(mapper); realm.addProtocolMapper(mapper);
} }

View file

@ -10,7 +10,7 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class AttributeMapperHelper { public class OIDCAttributeMapperHelper {
public static final String TOKEN_CLAIM_NAME = "Token Claim Name"; public static final String TOKEN_CLAIM_NAME = "Token Claim Name";
public static final String JSON_TYPE = "Claim JSON Type"; public static final String JSON_TYPE = "Claim JSON Type";

View file

@ -1,71 +0,0 @@
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.UserSessionModel;
import org.keycloak.representations.AccessToken;
import java.util.ArrayList;
import java.util.List;
/**
* Mappings UserSessionModel.note to an ID Token claim. Token claim name can be a full qualified nested object name,
* i.e. "address.country". This will create a nested
* json object within the toke claim.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OIDCClientSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
public static final String CLIENT_SESSION_NOTE = "ClientSession Note";
static {
ConfigProperty property;
property = new ConfigProperty();
property.setName(CLIENT_SESSION_NOTE);
property.setLabel(CLIENT_SESSION_NOTE);
property.setHelpText("Name of the note to map in the UserSessionModel");
configProperties.add(property);
property = new ConfigProperty();
property.setName(AttributeMapperHelper.TOKEN_CLAIM_NAME);
property.setLabel(AttributeMapperHelper.TOKEN_CLAIM_NAME);
property.setHelpText("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.");
configProperties.add(property);
}
public List<ConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return "oidc-client-session-note-mapper";
}
@Override
public String getDisplayType() {
return "ClientSession Note";
}
@Override
public String getDisplayCategory() {
return TOKEN_MAPPER_CATEGORY;
}
@Override
public String getHelpText() {
return "Map a temporary note that is attached to the ClientSession to a token claim.";
}
@Override
public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) {
String note = mappingModel.getConfig().get(CLIENT_SESSION_NOTE);
String noteValue = clientSession.getNote(note);
AttributeMapperHelper.mapClaim(token, mappingModel, noteValue);
return token;
}
}

View file

@ -5,6 +5,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import java.util.ArrayList; import java.util.ArrayList;
@ -21,18 +22,17 @@ import java.util.List;
public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>(); private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
public static final String USER_MODEL_ATTRIBUTE_NAME = "UserModel Attribute Name";
static { static {
ConfigProperty property; ConfigProperty property;
property = new ConfigProperty(); property = new ConfigProperty();
property.setName(USER_MODEL_ATTRIBUTE_NAME); property.setName(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_NAME);
property.setLabel(USER_MODEL_ATTRIBUTE_NAME); property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_NAME);
property.setHelpText("Name of stored user attribute which is the name of an attribute within the UserModel.attribute map."); property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
configProperties.add(property); configProperties.add(property);
property = new ConfigProperty(); property = new ConfigProperty();
property.setName(AttributeMapperHelper.TOKEN_CLAIM_NAME); property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
property.setLabel(AttributeMapperHelper.TOKEN_CLAIM_NAME); property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
property.setHelpText("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."); property.setHelpText("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.");
configProperties.add(property); configProperties.add(property);
@ -69,10 +69,10 @@ public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implemen
public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) { UserSessionModel userSession, ClientSessionModel clientSession) {
UserModel user = userSession.getUser(); UserModel user = userSession.getUser();
String attributeName = mappingModel.getConfig().get(USER_MODEL_ATTRIBUTE_NAME); String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_NAME);
String attributeValue = user.getAttribute(attributeName); String attributeValue = user.getAttribute(attributeName);
if (attributeValue == null) return token; if (attributeValue == null) return token;
AttributeMapperHelper.mapClaim(token, mappingModel, attributeValue); OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attributeValue);
return token; return token;
} }

View file

@ -5,9 +5,9 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -21,18 +21,17 @@ import java.util.List;
*/ */
public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>(); private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
public static final String USER_MODEL_PROPERTY = "User Property";
static { static {
ConfigProperty property; ConfigProperty property;
property = new ConfigProperty(); property = new ConfigProperty();
property.setName(USER_MODEL_PROPERTY); property.setName(ProtocolMapperUtils.USER_MODEL_PROPERTY);
property.setLabel(USER_MODEL_PROPERTY); property.setLabel(ProtocolMapperUtils.USER_MODEL_PROPERTY);
property.setHelpText("Name of the property method in the UserModel interface. For example, a value of 'email' would reference the UserModel.getEmail() method."); property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT);
configProperties.add(property); configProperties.add(property);
property = new ConfigProperty(); property = new ConfigProperty();
property.setName(AttributeMapperHelper.TOKEN_CLAIM_NAME); property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
property.setLabel(AttributeMapperHelper.TOKEN_CLAIM_NAME); property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
property.setHelpText("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."); property.setHelpText("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.");
configProperties.add(property); configProperties.add(property);
@ -68,31 +67,11 @@ public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements O
public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) { UserSessionModel userSession, ClientSessionModel clientSession) {
UserModel user = userSession.getUser(); UserModel user = userSession.getUser();
String propertyName = mappingModel.getConfig().get(USER_MODEL_PROPERTY); String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_PROPERTY);
String propertyValue = getUserModelValue(user,propertyName); String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName);
AttributeMapperHelper.mapClaim(token, mappingModel, propertyValue); OIDCAttributeMapperHelper.mapClaim(token, mappingModel, propertyValue);
return token; return token;
} }
protected String getUserModelValue(UserModel user, String propertyName) {
String methodName = "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
try {
Method method = UserModel.class.getMethod(methodName);
Object val = method.invoke(user);
if (val != null) return val.toString();
} catch (Exception ignore) {
}
methodName = "is" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
try {
Method method = UserModel.class.getMethod(methodName);
Object val = method.invoke(user);
if (val != null) return val.toString();
} catch (Exception ignore) {
}
return null;
}
} }

View file

@ -1,72 +0,0 @@
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.UserSessionModel;
import org.keycloak.representations.AccessToken;
import java.util.ArrayList;
import java.util.List;
/**
* Mappings UserSessionModel.note to an ID Token claim. Token claim name can be a full qualified nested object name,
* i.e. "address.country". This will create a nested
* json object within the toke claim.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OIDCUserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
public static final String USER_SESSION_NOTE = "UserSession Note";
static {
ConfigProperty property;
property = new ConfigProperty();
property.setName(USER_SESSION_NOTE);
property.setLabel("UserSession Note");
property.setHelpText("Name of the note to map in the UserSessionModel");
configProperties.add(property);
property = new ConfigProperty();
property.setName(AttributeMapperHelper.TOKEN_CLAIM_NAME);
property.setLabel(AttributeMapperHelper.TOKEN_CLAIM_NAME);
property.setHelpText("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.");
configProperties.add(property);
}
public List<ConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return "oidc-user-session-note-mapper";
}
@Override
public String getDisplayType() {
return "UserSession Note";
}
@Override
public String getDisplayCategory() {
return TOKEN_MAPPER_CATEGORY;
}
@Override
public String getHelpText() {
return "Map a temporary note that is attached to the UserSession to a token claim.";
}
@Override
public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) {
String note = mappingModel.getConfig().get(USER_SESSION_NOTE);
String noteValue = userSession.getNote(note);
AttributeMapperHelper.mapClaim(token, mappingModel, noteValue);
return token;
}
}

View file

@ -1,8 +1,6 @@
org.keycloak.protocol.oidc.mappers.OIDCUserAttributeMapper org.keycloak.protocol.oidc.mappers.OIDCUserAttributeMapper
org.keycloak.protocol.oidc.mappers.OIDCClientSessionNoteMapper
org.keycloak.protocol.oidc.mappers.OIDCFullNameMapper org.keycloak.protocol.oidc.mappers.OIDCFullNameMapper
org.keycloak.protocol.oidc.mappers.OIDCUserModelMapper org.keycloak.protocol.oidc.mappers.OIDCUserModelMapper
org.keycloak.protocol.oidc.mappers.OIDCUserSessionNoteMapper
org.keycloak.protocol.oidc.mappers.OIDCAddressMapper org.keycloak.protocol.oidc.mappers.OIDCAddressMapper

View file

@ -157,8 +157,7 @@ public class AccountTest {
}); });
} }
@Test //@Test @Ignore
@Ignore
public void runit() throws Exception { public void runit() throws Exception {
Thread.sleep(10000000); Thread.sleep(10000000);
} }