hardcoded saml mappers
This commit is contained in:
parent
7ba0681731
commit
d4468913dd
8 changed files with 225 additions and 10 deletions
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue