saml role list mapper
This commit is contained in:
parent
1de285b724
commit
5c6c30fef4
14 changed files with 344 additions and 78 deletions
|
@ -19,10 +19,8 @@ import org.picketlink.identity.federation.saml.v2.assertion.AuthnStatementType;
|
|||
import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.picketlink.common.util.StringUtil.isNotNull;
|
||||
|
||||
|
@ -36,7 +34,6 @@ import static org.picketlink.common.util.StringUtil.isNotNull;
|
|||
public class SALM2LoginResponseBuilder {
|
||||
protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
|
||||
|
||||
protected List<String> roles = new LinkedList<String>();
|
||||
protected String destination;
|
||||
protected String issuer;
|
||||
protected String nameId;
|
||||
|
@ -68,18 +65,6 @@ public class SALM2LoginResponseBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SALM2LoginResponseBuilder roles(List<String> roles) {
|
||||
this.roles = roles;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SALM2LoginResponseBuilder roles(String... roles) {
|
||||
for (String role : roles) {
|
||||
this.roles.add(role);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SALM2LoginResponseBuilder authMethod(String authMethod) {
|
||||
this.authMethod = authMethod;
|
||||
return this;
|
||||
|
@ -155,12 +140,6 @@ public class SALM2LoginResponseBuilder {
|
|||
|
||||
assertion.addStatement(authnStatement);
|
||||
}
|
||||
|
||||
if (roles != null && !roles.isEmpty()) {
|
||||
AttributeStatementType attrStatement = StatementUtil.createAttributeStatementForRoles(roles, multiValuedRoles);
|
||||
assertion.addStatement(attrStatement);
|
||||
}
|
||||
|
||||
return responseType;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.keycloak.protocol.LoginProtocol;
|
|||
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.SAMLRoleListMapper;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.managers.ResourceAdminManager;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
|
@ -29,7 +30,6 @@ import org.picketlink.common.constants.JBossSAMLURIConstants;
|
|||
import org.picketlink.common.exceptions.ConfigurationException;
|
||||
import org.picketlink.common.exceptions.ParsingException;
|
||||
import org.picketlink.common.exceptions.ProcessingException;
|
||||
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;
|
||||
|
@ -41,6 +41,8 @@ import javax.ws.rs.core.Response;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -258,26 +260,38 @@ public class SamlProtocol implements LoginProtocol {
|
|||
.requestIssuer(clientSession.getClient().getClientId())
|
||||
.nameIdentifier(nameIdFormat, nameId)
|
||||
.authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
|
||||
initClaims(builder, clientSession.getClient(), userSession.getUser());
|
||||
if (clientSession.getRoles() != null) {
|
||||
if (multivaluedRoles(client)) {
|
||||
builder.multiValuedRoles(true);
|
||||
}
|
||||
for (String roleId : clientSession.getRoles()) {
|
||||
// todo need a role mapping
|
||||
RoleModel roleModel = clientSession.getRealm().getRoleById(roleId);
|
||||
builder.roles(roleModel.getName());
|
||||
}
|
||||
}
|
||||
if (!includeAuthnStatement(client)) {
|
||||
builder.disableAuthnStatement(true);
|
||||
}
|
||||
|
||||
List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers = new LinkedList<>();
|
||||
List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> loginResponseMappers = new LinkedList<>();
|
||||
ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper = null;
|
||||
|
||||
Set<ProtocolMapperModel> mappings = client.getProtocolMappers();
|
||||
for (ProtocolMapperModel mapping : mappings) {
|
||||
if (!mapping.getProtocol().equals(SamlProtocol.LOGIN_PROTOCOL)) continue;
|
||||
|
||||
ProtocolMapper mapper = (ProtocolMapper)session.getKeycloakSessionFactory().getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
|
||||
if (mapper == null) continue;
|
||||
if (mapper instanceof SAMLAttributeStatementMapper) {
|
||||
attributeStatementMappers.add(new ProtocolMapperProcessor<SAMLAttributeStatementMapper>((SAMLAttributeStatementMapper)mapper, mapping));
|
||||
}
|
||||
if (mapper instanceof SAMLLoginResponseMapper) {
|
||||
loginResponseMappers.add(new ProtocolMapperProcessor<SAMLLoginResponseMapper>((SAMLLoginResponseMapper)mapper, mapping));
|
||||
}
|
||||
if (mapper instanceof SAMLRoleListMapper) {
|
||||
roleListMapper = new ProtocolMapperProcessor<SAMLRoleListMapper>((SAMLRoleListMapper)mapper, mapping);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Document samlDocument = null;
|
||||
try {
|
||||
ResponseType samlModel = builder.buildModel();
|
||||
transformAttributeStatement(session, samlModel, client, userSession, clientSession);
|
||||
samlModel = transformLoginResponse(session, samlModel, client, userSession, clientSession);
|
||||
transformAttributeStatement(attributeStatementMappers, samlModel, session, userSession, clientSession);
|
||||
populateRoles(roleListMapper, samlModel, session, userSession, clientSession);
|
||||
samlModel = transformLoginResponse(loginResponseMappers, samlModel, session, userSession, clientSession);
|
||||
samlDocument = builder.buildDocument(samlModel);
|
||||
} catch (Exception e) {
|
||||
logger.error("failed", e);
|
||||
|
@ -348,54 +362,50 @@ public class SamlProtocol implements LoginProtocol {
|
|||
return "true".equals(client.getAttribute(SAML_ENCRYPT));
|
||||
}
|
||||
|
||||
public void initClaims(SALM2LoginResponseBuilder builder, ClientModel model, UserModel user) {
|
||||
if (ClaimMask.hasEmail(model.getAllowedClaimsMask())) {
|
||||
//builder.attribute(X500SAMLProfileConstants.EMAIL_ADDRESS.getFriendlyName(), user.getEmail());
|
||||
}
|
||||
if (ClaimMask.hasName(model.getAllowedClaimsMask())) {
|
||||
//builder.attribute(X500SAMLProfileConstants.GIVEN_NAME.getFriendlyName(), user.getFirstName());
|
||||
//builder.attribute(X500SAMLProfileConstants.SURNAME.getFriendlyName(), user.getLastName());
|
||||
}
|
||||
if (ClaimMask.hasUsername(model.getAllowedClaimsMask())) {
|
||||
//builder.attribute(X500SAMLProfileConstants.USERID.getFriendlyName(), user.getUsername());
|
||||
public static class ProtocolMapperProcessor<T> {
|
||||
final public T mapper;
|
||||
final public ProtocolMapperModel model;
|
||||
|
||||
public ProtocolMapperProcessor(T mapper, ProtocolMapperModel model) {
|
||||
this.mapper = mapper;
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseType transformLoginResponse(KeycloakSession session, ResponseType response, ClientModel client,
|
||||
UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
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 SAMLLoginResponseMapper)) continue;
|
||||
response = ((SAMLLoginResponseMapper)mapper).transformLoginResponse(response, mapping, session, userSession, clientSession);
|
||||
|
||||
|
||||
public void transformAttributeStatement(List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers,
|
||||
ResponseType response,
|
||||
KeycloakSession session,
|
||||
UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
AssertionType assertion = response.getAssertions().get(0).getAssertion();
|
||||
AttributeStatementType attributeStatement = new AttributeStatementType();
|
||||
assertion.addStatement(attributeStatement);
|
||||
for (ProtocolMapperProcessor<SAMLAttributeStatementMapper> processor : attributeStatementMappers) {
|
||||
processor.mapper.transformAttributeStatement(attributeStatement, processor.model, session, userSession, clientSession);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseType transformLoginResponse(List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> mappers,
|
||||
ResponseType response,
|
||||
KeycloakSession session,
|
||||
UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
for (ProtocolMapperProcessor<SAMLLoginResponseMapper> processor : mappers) {
|
||||
response = processor.mapper.transformLoginResponse(response, processor.model, session, userSession, clientSession);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
public void transformAttributeStatement(KeycloakSession session, ResponseType response, ClientModel client,
|
||||
UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
AttributeStatementType attributeStatement = new AttributeStatementType();
|
||||
|
||||
public void populateRoles(ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper,
|
||||
ResponseType response,
|
||||
KeycloakSession session,
|
||||
UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
if (roleListMapper == null) return;
|
||||
AssertionType assertion = response.getAssertions().get(0).getAssertion();
|
||||
AttributeStatementType attributeStatement = new AttributeStatementType();
|
||||
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);
|
||||
}
|
||||
roleListMapper.mapper.mapRoles(attributeStatement, roleListMapper.model, session, userSession, clientSession);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Response consentDenied(ClientSessionModel clientSession) {
|
||||
return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
|
||||
|
|
|
@ -8,6 +8,8 @@ import org.keycloak.models.ProtocolMapperModel;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.AbstractLoginProtocolFactory;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
|
||||
import org.keycloak.protocol.saml.mappers.SAMLBasicRoleListMapper;
|
||||
import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.picketlink.common.constants.JBossSAMLURIConstants;
|
||||
|
@ -75,6 +77,9 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
X500SAMLProfileConstants.SURNAME.getFriendlyName(),
|
||||
true, "family name");
|
||||
builtins.add(model);
|
||||
model = SAMLBasicRoleListMapper.create("role list", "Role", AttributeStatementHelper.BASIC, null, false);
|
||||
builtins.add(model);
|
||||
defaultBuiltins.add(model);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.protocol.saml;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Signature;
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,6 @@ import org.keycloak.Config;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.protocol.ProtocolMapper;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.keycloak.protocol.saml.mappers;
|
||||
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.ProtocolMapper;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
|
@ -31,6 +30,12 @@ public class AttributeStatementHelper {
|
|||
|
||||
public static void addAttribute(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel,
|
||||
String attributeValue) {
|
||||
AttributeType attribute = createAttributeType(mappingModel);
|
||||
attribute.addAttributeValue(attributeValue);
|
||||
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attribute));
|
||||
}
|
||||
|
||||
public static AttributeType createAttributeType(ProtocolMapperModel mappingModel) {
|
||||
String attributeName = mappingModel.getConfig().get(SAML_ATTRIBUTE_NAME);
|
||||
AttributeType attribute = new AttributeType(attributeName);
|
||||
String attributeType = mappingModel.getConfig().get(SAML_ATTRIBUTE_NAMEFORMAT);
|
||||
|
@ -40,8 +45,7 @@ public class AttributeStatementHelper {
|
|||
attribute.setNameFormat(attributeNameFormat);
|
||||
String friendlyName = mappingModel.getConfig().get(FRIENDLY_NAME);
|
||||
if (friendlyName != null && !friendlyName.trim().equals("")) attribute.setFriendlyName(friendlyName);
|
||||
attribute.addAttributeValue(attributeValue);
|
||||
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attribute));
|
||||
return attribute;
|
||||
}
|
||||
|
||||
public static void setConfigProperties(List<ProtocolMapper.ConfigProperty> configProperties) {
|
||||
|
|
|
@ -5,7 +5,6 @@ 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>
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
package org.keycloak.protocol.saml.mappers;
|
||||
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.ProtocolMapper;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
|
||||
import org.picketlink.identity.federation.saml.v2.assertion.AttributeType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class SAMLBasicRoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRoleListMapper {
|
||||
public static final String PROVIDER_ID = "saml-role-list-mapper";
|
||||
public static final String SINGLE_ROLE_ATTRIBUTE = "single";
|
||||
|
||||
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
|
||||
|
||||
static {
|
||||
ConfigProperty property;
|
||||
property = new ConfigProperty();
|
||||
property.setName(AttributeStatementHelper.SAML_ATTRIBUTE_NAME);
|
||||
property.setLabel("Role attribute name");
|
||||
property.setDefaultValue("Role");
|
||||
property.setHelpText("Name of the SAML attribute you want to put your roles into. i.e. 'Role', 'memberOf'.");
|
||||
configProperties.add(property);
|
||||
property = new ProtocolMapper.ConfigProperty();
|
||||
property.setName(AttributeStatementHelper.FRIENDLY_NAME);
|
||||
property.setLabel(AttributeStatementHelper.FRIENDLY_NAME_LABEL);
|
||||
property.setHelpText(AttributeStatementHelper.FRIENDLY_NAME_HELP_TEXT);
|
||||
configProperties.add(property);
|
||||
property = new ProtocolMapper.ConfigProperty();
|
||||
property.setName(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT);
|
||||
property.setLabel("SAML Attribute NameFormat");
|
||||
property.setHelpText("SAML Attribute NameFormat. Can be basic, URI reference, or unspecified.");
|
||||
List<String> types = new ArrayList(3);
|
||||
types.add(AttributeStatementHelper.BASIC);
|
||||
types.add(AttributeStatementHelper.URI_REFERENCE);
|
||||
types.add(AttributeStatementHelper.UNSPECIFIED);
|
||||
property.setType(ProtocolMapper.ConfigProperty.LIST_TYPE);
|
||||
property.setDefaultValue(types);
|
||||
configProperties.add(property);
|
||||
property = new ConfigProperty();
|
||||
property.setName(SINGLE_ROLE_ATTRIBUTE);
|
||||
property.setLabel("Single Role Attribute");
|
||||
property.setType(ConfigProperty.BOOLEAN_TYPE);
|
||||
property.setDefaultValue("true");
|
||||
property.setHelpText("If true, all roles will be stored under one attribute with multiple attribute values.");
|
||||
configProperties.add(property);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Role Mapper";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Role list";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Role names are stored in an attribute value. There is either one attribute with multiple attribute values, or an attribute per role name depending on how you configure it. You can also specify the attribute name i.e. 'Role' or 'memberOf' being examples.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapRoles(AttributeStatementType roleAttributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
String single = mappingModel.getConfig().get(SINGLE_ROLE_ATTRIBUTE);
|
||||
boolean singleAttribute = Boolean.parseBoolean(single);
|
||||
|
||||
Map<ProtocolMapperModel, SAMLRoleNameMapper> roleNameMappers = new HashMap<>();
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
for (ProtocolMapperModel mapping : clientSession.getClient().getProtocolMappers()) {
|
||||
if (!mapping.getProtocol().equals(SamlProtocol.LOGIN_PROTOCOL)) continue;
|
||||
|
||||
ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
|
||||
if (mapper == null || !(mapper instanceof SAMLRoleNameMapper)) continue;
|
||||
roleNameMappers.put(mapping, (SAMLRoleNameMapper)mapper);
|
||||
}
|
||||
|
||||
AttributeType singleAttributeType = null;
|
||||
for (String roleId : clientSession.getRoles()) {
|
||||
// todo need a role mapping
|
||||
RoleModel roleModel = clientSession.getRealm().getRoleById(roleId);
|
||||
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));
|
||||
}
|
||||
String roleName = roleModel.getName();
|
||||
for (Map.Entry<ProtocolMapperModel, SAMLRoleNameMapper> entry : roleNameMappers.entrySet()) {
|
||||
String newName = entry.getValue().mapName(entry.getKey(), roleModel);
|
||||
if (newName != null) {
|
||||
roleName = newName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
attributeType.addAttributeValue(roleName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel create(String name, String samlAttributeName, String nameFormat, String friendlyName, boolean singleAttribute) {
|
||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||
mapper.setName(name);
|
||||
mapper.setProtocolMapper(PROVIDER_ID);
|
||||
mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
|
||||
mapper.setConsentRequired(false);
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
config.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, samlAttributeName);
|
||||
if (friendlyName != null) {
|
||||
config.put(AttributeStatementHelper.FRIENDLY_NAME, friendlyName);
|
||||
}
|
||||
if (nameFormat != null) {
|
||||
config.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT, nameFormat);
|
||||
}
|
||||
config.put(SINGLE_ROLE_ATTRIBUTE, Boolean.toString(singleAttribute));
|
||||
mapper.setConfig(config);
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package org.keycloak.protocol.saml.mappers;
|
||||
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Map an assigned role to a different position and name in the token
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class SAMLBasicRoleNameMapper extends AbstractOIDCProtocolMapper implements SAMLRoleNameMapper {
|
||||
|
||||
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
|
||||
|
||||
public static final String ROLE_CONFIG = "role";
|
||||
public static String NEW_ROLE_NAME = "new.role.name";
|
||||
|
||||
static {
|
||||
ConfigProperty property;
|
||||
property = new ConfigProperty();
|
||||
property.setName(ROLE_CONFIG);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role name you want changed. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
property = new ConfigProperty();
|
||||
property.setName(NEW_ROLE_NAME);
|
||||
property.setLabel("New Role Name");
|
||||
property.setHelpText("The new role name.");
|
||||
property.setType(ConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "saml-role-name-mapper";
|
||||
|
||||
|
||||
public List<ConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Role Name Mapper";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Role Mapper";
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Map an assigned role to a new name";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mapName(ProtocolMapperModel model, RoleModel roleModel) {
|
||||
RoleContainerModel container = roleModel.getContainer();
|
||||
ApplicationModel app = null;
|
||||
if (container instanceof ApplicationModel) {
|
||||
app = ((ApplicationModel) container);
|
||||
}
|
||||
String role = model.getConfig().get(ROLE_CONFIG);
|
||||
String newName = model.getConfig().get(NEW_ROLE_NAME);
|
||||
String appName = null;
|
||||
int scopeIndex = role.indexOf('.');
|
||||
if (scopeIndex > -1) {
|
||||
if (app == null) return null;
|
||||
appName = role.substring(0, scopeIndex);
|
||||
if (!app.getName().equals(appName)) return null;
|
||||
role = role.substring(scopeIndex + 1);
|
||||
} else {
|
||||
if (app != null) return null;
|
||||
}
|
||||
if (roleModel.getName().equals(role)) return newName;
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ 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 org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.keycloak.protocol.saml.mappers;
|
||||
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface SAMLRoleNameMapper {
|
||||
public String mapName(ProtocolMapperModel model, RoleModel role);
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
org.keycloak.protocol.saml.mappers.SAMLBasicRoleListMapper
|
||||
org.keycloak.protocol.saml.mappers.SAMLBasicRoleNameMapper
|
||||
org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper
|
||||
org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package org.keycloak.protocol;
|
||||
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
|
|
@ -256,6 +256,17 @@
|
|||
"attribute.name": "phone",
|
||||
"attribute.nameformat": "Basic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "role-list",
|
||||
"protocol": "saml",
|
||||
"protocolMapper": "saml-role-list-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"attribute.name": "Role",
|
||||
"attribute.nameformat": "Basic",
|
||||
"single": "false"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue