hardcoded saml mappers

This commit is contained in:
Bill Burke 2015-03-09 07:55:20 -04:00
parent 7ba0681731
commit d4468913dd
8 changed files with 225 additions and 10 deletions

View file

@ -73,14 +73,14 @@ public class AttributeStatementHelper {
} }
public static ProtocolMapperModel createAttributeMapper(String name, String userAttribute, String samlAttributeName, String nameFormat, String friendlyName, boolean consentRequired, String consentText, String mapperId) { public static ProtocolMapperModel createAttributeMapper(String name, String userAttribute, String samlAttributeName, String nameFormat, String friendlyName, boolean consentRequired, String consentText, String mapperId) {
ProtocolMapperModel mapper = mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(mapperId); mapper.setProtocolMapper(mapperId);
mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL); mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
mapper.setConsentRequired(consentRequired); mapper.setConsentRequired(consentRequired);
mapper.setConsentText(consentText); mapper.setConsentText(consentText);
Map<String, String> config = new HashMap<String, String>(); Map<String, String> config = new HashMap<String, String>();
config.put(ProtocolMapperUtils.USER_ATTRIBUTE, userAttribute); if (userAttribute != null) config.put(ProtocolMapperUtils.USER_ATTRIBUTE, userAttribute);
config.put(SAML_ATTRIBUTE_NAME, samlAttributeName); config.put(SAML_ATTRIBUTE_NAME, samlAttributeName);
if (friendlyName != null) { if (friendlyName != null) {
config.put(FRIENDLY_NAME, friendlyName); config.put(FRIENDLY_NAME, friendlyName);

View file

@ -0,0 +1,85 @@
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 HardcodedAttributeMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
public static final String PROVIDER_ID = "saml-hardcode-attribute-mapper";
public static final String ATTRIBUTE_VALUE = "attribute.value";
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
static {
ConfigProperty property;
property = new ConfigProperty();
property.setName(ProtocolMapperUtils.USER_ATTRIBUTE);
property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL);
property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
configProperties.add(property);
AttributeStatementHelper.setConfigProperties(configProperties);
property = new ConfigProperty();
property.setName(ATTRIBUTE_VALUE);
property.setLabel("Attribute value");
property.setType(ConfigProperty.STRING_TYPE);
property.setHelpText("Value of the attribute you want to hard code.");
configProperties.add(property);
}
public List<ConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String getDisplayType() {
return "Hardcoded attribute";
}
@Override
public String getDisplayCategory() {
return AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY;
}
@Override
public String getHelpText() {
return "Hardcode an attribute into the SAML Assertion.";
}
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
String attributeValue = mappingModel.getConfig().get(ATTRIBUTE_VALUE);
AttributeStatementHelper.addAttribute(attributeStatement, mappingModel, attributeValue);
}
public static ProtocolMapperModel create(String name,
String samlAttributeName, String nameFormat, String friendlyName, String value,
boolean consentRequired, String consentText) {
String mapperId = PROVIDER_ID;
ProtocolMapperModel model = AttributeStatementHelper.createAttributeMapper(name, null, samlAttributeName, nameFormat, friendlyName,
consentRequired, consentText, mapperId);
model.getConfig().put(ATTRIBUTE_VALUE, value);
return model;
}
}

View file

@ -0,0 +1,76 @@
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.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.saml.SamlProtocol;
import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 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 HardcodedRole extends AbstractSAMLProtocolMapper {
public static final String PROVIDER_ID = "saml-hardcode-role-mapper";
public static final String ATTRIBUTE_VALUE = "attribute.value";
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
static {
ConfigProperty property;
property = new ConfigProperty();
property.setName("role");
property.setLabel("Role");
property.setHelpText("Role name you want to hardcode.");
property.setType(ConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public List<ConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String getDisplayType() {
return "Hardcoded role";
}
@Override
public String getDisplayCategory() {
return AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY;
}
@Override
public String getHelpText() {
return "Hardcode role into SAML Assertion.";
}
public static ProtocolMapperModel create(String name,
String role) {
String mapperId = PROVIDER_ID;
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(mapperId);
mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
Map<String, String> config = new HashMap<String, String>();
config.put("role", role);
mapper.setConfig(config);
return mapper;
}
}

View file

@ -14,6 +14,7 @@ import org.picketlink.identity.federation.saml.v2.assertion.AttributeType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -92,17 +93,33 @@ public class SAMLBasicRoleListMapper extends AbstractSAMLProtocolMapper implemen
String single = mappingModel.getConfig().get(SINGLE_ROLE_ATTRIBUTE); String single = mappingModel.getConfig().get(SINGLE_ROLE_ATTRIBUTE);
boolean singleAttribute = Boolean.parseBoolean(single); boolean singleAttribute = Boolean.parseBoolean(single);
Map<ProtocolMapperModel, SAMLRoleNameMapper> roleNameMappers = new HashMap<>(); List<SamlProtocol.ProtocolMapperProcessor<SAMLRoleNameMapper>> roleNameMappers = new LinkedList<>();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory(); KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
AttributeType singleAttributeType = null;
for (ProtocolMapperModel mapping : clientSession.getClient().getProtocolMappers()) { for (ProtocolMapperModel mapping : clientSession.getClient().getProtocolMappers()) {
if (!mapping.getProtocol().equals(SamlProtocol.LOGIN_PROTOCOL)) continue; if (!mapping.getProtocol().equals(SamlProtocol.LOGIN_PROTOCOL)) continue;
ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper()); ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
if (mapper == null || !(mapper instanceof SAMLRoleNameMapper)) continue; if (mapper == null) continue;
roleNameMappers.put(mapping, (SAMLRoleNameMapper)mapper); if (mapper instanceof SAMLRoleNameMapper) {
roleNameMappers.add(new SamlProtocol.ProtocolMapperProcessor<>((SAMLRoleNameMapper) mapper,mapping));
}
if (mapper instanceof HardcodedRole) {
AttributeType attributeType = null;
if (singleAttribute) {
if (singleAttributeType == null) {
singleAttributeType = AttributeStatementHelper.createAttributeType(mappingModel);
roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(singleAttributeType));
}
attributeType = singleAttributeType;
} else {
attributeType = AttributeStatementHelper.createAttributeType(mappingModel);
roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType));
}
attributeType.addAttributeValue(mapping.getConfig().get("role"));
}
} }
AttributeType singleAttributeType = null;
for (String roleId : clientSession.getRoles()) { for (String roleId : clientSession.getRoles()) {
// todo need a role mapping // todo need a role mapping
RoleModel roleModel = clientSession.getRealm().getRoleById(roleId); RoleModel roleModel = clientSession.getRealm().getRoleById(roleId);
@ -118,8 +135,8 @@ public class SAMLBasicRoleListMapper extends AbstractSAMLProtocolMapper implemen
roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType)); roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType));
} }
String roleName = roleModel.getName(); String roleName = roleModel.getName();
for (Map.Entry<ProtocolMapperModel, SAMLRoleNameMapper> entry : roleNameMappers.entrySet()) { for (SamlProtocol.ProtocolMapperProcessor<SAMLRoleNameMapper> entry : roleNameMappers) {
String newName = entry.getValue().mapName(entry.getKey(), roleModel); String newName = entry.mapper.mapName(entry.model, roleModel);
if (newName != null) { if (newName != null) {
roleName = newName; roleName = newName;
break; break;

View file

@ -5,9 +5,12 @@ import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper; import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper;
import org.keycloak.protocol.saml.SamlProtocol;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Map an assigned role to a different position and name in the token * Map an assigned role to a different position and name in the token
@ -88,4 +91,21 @@ public class SAMLBasicRoleNameMapper extends AbstractOIDCProtocolMapper implemen
if (roleModel.getName().equals(role)) return newName; if (roleModel.getName().equals(role)) return newName;
return null; return null;
} }
public static ProtocolMapperModel create(String name,
String role,
String newName) {
String mapperId = PROVIDER_ID;
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(mapperId);
mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
Map<String, String> config = new HashMap<String, String>();
config.put(ROLE_CONFIG, role);
config.put(NEW_ROLE_NAME, newName);
mapper.setConfig(config);
return mapper;
}
} }

View file

@ -1,5 +1,7 @@
org.keycloak.protocol.saml.mappers.SAMLBasicRoleListMapper org.keycloak.protocol.saml.mappers.SAMLBasicRoleListMapper
org.keycloak.protocol.saml.mappers.SAMLBasicRoleNameMapper org.keycloak.protocol.saml.mappers.SAMLBasicRoleNameMapper
org.keycloak.protocol.saml.mappers.HardcodedRole
org.keycloak.protocol.saml.mappers.HardcodedAttributeMapper
org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper
org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper

View file

@ -76,7 +76,7 @@ public class OIDCAddClaimMapper extends AbstractOIDCProtocolMapper implements OI
@Override @Override
public String getDisplayType() { public String getDisplayType() {
return "Hard coded claim"; return "Hardcoded claim";
} }
@Override @Override

View file

@ -16,7 +16,10 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.protocol.saml.mappers.AttributeStatementHelper; import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
import org.keycloak.protocol.saml.mappers.HardcodedAttributeMapper;
import org.keycloak.protocol.saml.mappers.HardcodedRole;
import org.keycloak.protocol.saml.mappers.SAMLBasicRoleListMapper; import org.keycloak.protocol.saml.mappers.SAMLBasicRoleListMapper;
import org.keycloak.protocol.saml.mappers.SAMLBasicRoleNameMapper;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.admin.AdminRoot; import org.keycloak.services.resources.admin.AdminRoot;
@ -265,6 +268,9 @@ public class SamlBindingTest {
app.addProtocolMapper(mapper); app.addProtocolMapper(mapper);
} }
} }
app.addProtocolMapper(HardcodedAttributeMapper.create("hardcoded-attribute", "hardcoded-attribute", "Basic", null, "hard", false, null));
app.addProtocolMapper(HardcodedRole.create("hardcoded-role", "hardcoded-role"));
app.addProtocolMapper(SAMLBasicRoleNameMapper.create("renamed-role","manager", "el-jefe"));
} }
}, "demo"); }, "demo");
@ -287,6 +293,8 @@ public class SamlBindingTest {
boolean userRole = false; boolean userRole = false;
boolean managerRole = false; boolean managerRole = false;
boolean single = false; boolean single = false;
boolean hardcodedRole = false;
boolean hardcodedAttribute = false;
for (AttributeStatementType statement : assertion.getAttributeStatements()) { for (AttributeStatementType statement : assertion.getAttributeStatements()) {
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) { for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
AttributeType attr = choice.getAttribute(); AttributeType attr = choice.getAttribute();
@ -294,14 +302,21 @@ public class SamlBindingTest {
if (single) Assert.fail("too many role attributes"); if (single) Assert.fail("too many role attributes");
single = true; single = true;
for (Object value : attr.getAttributeValue()) { for (Object value : attr.getAttributeValue()) {
if (value.equals("manager")) managerRole = true; if (value.equals("el-jefe")) managerRole = true;
if (value.equals("user")) userRole = true; if (value.equals("user")) userRole = true;
if (value.equals("hardcoded-role")) hardcodedRole = true;
} }
} else if (attr.getName().equals("hardcoded-attribute")) {
hardcodedAttribute = true;
Assert.assertEquals(attr.getAttributeValue().get(0), "hard");
} }
} }
} }
Assert.assertTrue(single);
Assert.assertTrue(hardcodedAttribute);
Assert.assertTrue(hardcodedRole);
Assert.assertTrue(userRole); Assert.assertTrue(userRole);
Assert.assertTrue(managerRole); Assert.assertTrue(managerRole);
} }