Merge pull request #1027 from patriot1burke/master
added mappers, test saml mappers
This commit is contained in:
commit
868c54b614
25 changed files with 728 additions and 422 deletions
|
@ -125,9 +125,11 @@ public class AccessToken extends IDToken {
|
|||
}
|
||||
|
||||
public Access addAccess(String service) {
|
||||
Access token = new Access();
|
||||
resourceAccess.put(service, token);
|
||||
return token;
|
||||
Access access = resourceAccess.get(service);
|
||||
if (access != null) return access;
|
||||
access = new Access();
|
||||
resourceAccess.put(service, access);
|
||||
return access;
|
||||
}
|
||||
|
||||
public AccessToken clientSession(String session) {
|
||||
|
|
|
@ -17,7 +17,7 @@ public class ProtocolMapperTypeRepresentation {
|
|||
protected String label;
|
||||
protected String helpText;
|
||||
protected String type;
|
||||
protected String defaultValue;
|
||||
protected Object defaultValue;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
|
@ -43,11 +43,11 @@ public class ProtocolMapperTypeRepresentation {
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
public String getDefaultValue() {
|
||||
public Object getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(String defaultValue) {
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,12 +78,17 @@
|
|||
<div data-ng-repeat="option in mapperType.properties" class="form-group">
|
||||
<label class="col-sm-2 control-label">{{option.label}} </label>
|
||||
|
||||
<div class="col-sm-4" data-ng-hide="option.type == 'boolean'">
|
||||
<div class="col-sm-4" data-ng-hide="option.type == 'boolean' || option.type == 'List'">
|
||||
<input class="form-control" type="text" data-ng-model="mapper.config[ option.name ]" >
|
||||
</div>
|
||||
<div class="col-sm-4" data-ng-show="option.type == 'boolean'">
|
||||
<input ng-model="mapper.config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchmodel />
|
||||
</div>
|
||||
<div class="col-sm-4" data-ng-show="option.type == 'List'">
|
||||
<select ng-model="mapper.config[ option.name ]" ng-options="data for data in option.defaultValue">
|
||||
<option value="" selected> Select one... </option>
|
||||
</select>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="{{option.helpText}}" class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -76,14 +76,19 @@
|
|||
<span tooltip-placement="right" tooltip="{{mapperType.helpText}}" class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div data-ng-repeat="option in mapperType.properties" class="form-group">
|
||||
<label class="col-sm-2 control-label">{{option.label}} </label>
|
||||
<label class="col-sm-2 control-label">{{option.label}}</label>
|
||||
|
||||
<div class="col-sm-4" data-ng-hide="option.type == 'boolean'">
|
||||
<div class="col-sm-4" data-ng-hide="option.type == 'boolean' || option.type == 'List'">
|
||||
<input class="form-control" type="text" data-ng-model="mapper.config[ option.name ]" >
|
||||
</div>
|
||||
<div class="col-sm-4" data-ng-show="option.type == 'boolean'">
|
||||
<input ng-model="mapper.config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchmodel />
|
||||
</div>
|
||||
<div class="col-sm-4" data-ng-show="option.type == 'List'">
|
||||
<select ng-model="mapper.config[ option.name ]" ng-options="data for data in option.defaultValue">
|
||||
<option value="" selected> Select one... </option>
|
||||
</select>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="{{option.helpText}}" class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -4,26 +4,18 @@ import org.keycloak.Config;
|
|||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.AbstractLoginProtocolFactory;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.LoginProtocolFactory;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
||||
import org.keycloak.protocol.oidc.mappers.OIDCUserModelMapper;
|
||||
import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
|
||||
import org.keycloak.protocol.saml.mappers.UserAttributeBasicAttributeStatementMapper;
|
||||
import org.keycloak.protocol.saml.mappers.UserModelUriReferenceAttributeStatementMapper;
|
||||
import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.picketlink.common.constants.JBossSAMLURIConstants;
|
||||
import org.picketlink.identity.federation.core.saml.v2.constants.X500SAMLProfileConstants;
|
||||
import org.picketlink.identity.federation.core.sts.PicketLinkCoreSTS;
|
||||
|
||||
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>
|
||||
|
@ -62,19 +54,25 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
|
||||
static {
|
||||
ProtocolMapperModel model;
|
||||
model = UserModelUriReferenceAttributeStatementMapper.createAttributeMapper("X500 email",
|
||||
model = UserPropertyAttributeStatementMapper.createAttributeMapper("X500 email",
|
||||
"email",
|
||||
X500SAMLProfileConstants.EMAIL.get(), X500SAMLProfileConstants.EMAIL.getFriendlyName(),
|
||||
X500SAMLProfileConstants.EMAIL.get(),
|
||||
JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(),
|
||||
X500SAMLProfileConstants.EMAIL.getFriendlyName(),
|
||||
true, "email");
|
||||
builtins.add(model);
|
||||
model = UserModelUriReferenceAttributeStatementMapper.createAttributeMapper("X500 givenName",
|
||||
model = UserPropertyAttributeStatementMapper.createAttributeMapper("X500 givenName",
|
||||
"firstName",
|
||||
X500SAMLProfileConstants.GIVEN_NAME.get(), X500SAMLProfileConstants.GIVEN_NAME.getFriendlyName(),
|
||||
X500SAMLProfileConstants.GIVEN_NAME.get(),
|
||||
JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(),
|
||||
X500SAMLProfileConstants.GIVEN_NAME.getFriendlyName(),
|
||||
true, "given name");
|
||||
builtins.add(model);
|
||||
model = UserModelUriReferenceAttributeStatementMapper.createAttributeMapper("X500 surname",
|
||||
model = UserPropertyAttributeStatementMapper.createAttributeMapper("X500 surname",
|
||||
"lastName",
|
||||
X500SAMLProfileConstants.SURNAME.get(), X500SAMLProfileConstants.SURNAME.getFriendlyName(),
|
||||
X500SAMLProfileConstants.SURNAME.get(),
|
||||
JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(),
|
||||
X500SAMLProfileConstants.SURNAME.getFriendlyName(),
|
||||
true, "family name");
|
||||
builtins.add(model);
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ 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.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -18,19 +19,24 @@ import java.util.Map;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AttributeStatementHelper {
|
||||
public static final String SAML_ATTRIBUTE_NAME = "SAML Attribute Name";
|
||||
public static final String SAML_ATTRIBUTE_NAME = "attribute.name";
|
||||
public static final String ATTRIBUTE_STATEMENT_CATEGORY = "AttributeStatement Mapper";
|
||||
public static final String URI_REFERENCE_LABEL = "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_LABEL = "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 = "friendly.name";
|
||||
public static final String FRIENDLY_NAME_LABEL = "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 final String SAML_ATTRIBUTE_NAMEFORMAT = "attribute.nameformat";
|
||||
public static final String BASIC = "Basic";
|
||||
public static final String URI_REFERENCE = "URI Reference";
|
||||
public static final String UNSPECIFIED = "Unspecified";
|
||||
|
||||
public static void addAttribute(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel,
|
||||
String attributeNameFormat, String attributeValue) {
|
||||
String attributeValue) {
|
||||
String attributeName = mappingModel.getConfig().get(SAML_ATTRIBUTE_NAME);
|
||||
AttributeType attribute = new AttributeType(attributeName);
|
||||
String attributeType = mappingModel.getConfig().get(SAML_ATTRIBUTE_NAMEFORMAT);
|
||||
String attributeNameFormat = JBossSAMLURIConstants.ATTRIBUTE_FORMAT_BASIC.get();
|
||||
if ("URI Reference".equals(attributeType)) attributeNameFormat = JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get();
|
||||
else if ("Unspecified".equals(attributeType)) attributeNameFormat = "urn:oasis:names:tc:SAML2.0:attrname-format:unspecified";
|
||||
attribute.setNameFormat(attributeNameFormat);
|
||||
String friendlyName = mappingModel.getConfig().get(FRIENDLY_NAME);
|
||||
if (friendlyName != null && !friendlyName.trim().equals("")) attribute.setFriendlyName(friendlyName);
|
||||
|
@ -38,43 +44,31 @@ public class AttributeStatementHelper {
|
|||
attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attribute));
|
||||
}
|
||||
|
||||
public static void addUriReferenceAttribute(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, String attributeValue) {
|
||||
String attributeNameFormat = JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get();
|
||||
addAttribute(attributeStatement, mappingModel, attributeNameFormat, attributeValue);
|
||||
}
|
||||
public static void setConfigProperties(List<ProtocolMapper.ConfigProperty> configProperties) {
|
||||
ProtocolMapper.ConfigProperty 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_NAME);
|
||||
property.setLabel("SAML Attribute Name");
|
||||
property.setHelpText("SAML Attribute Name");
|
||||
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);
|
||||
|
||||
public static void addBasicAttribute(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, String attributeValue) {
|
||||
addAttribute(attributeStatement, mappingModel, JBossSAMLURIConstants.ATTRIBUTE_FORMAT_BASIC.get(), attributeValue);
|
||||
}
|
||||
|
||||
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(SAML_ATTRIBUTE_NAME);
|
||||
property.setLabel(URI_REFERENCE_LABEL);
|
||||
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(SAML_ATTRIBUTE_NAME);
|
||||
property.setLabel(BASIC_LABEL);
|
||||
property.setHelpText(BASIC_HELP_TEXT);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel createAttributeMapper(String name, String userAttribute, String samlAttributeName, 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();
|
||||
mapper.setName(name);
|
||||
mapper.setProtocolMapper(mapperId);
|
||||
|
@ -87,6 +81,9 @@ public class AttributeStatementHelper {
|
|||
if (friendlyName != null) {
|
||||
config.put(FRIENDLY_NAME, friendlyName);
|
||||
}
|
||||
if (nameFormat != null) {
|
||||
config.put(SAML_ATTRIBUTE_NAMEFORMAT, nameFormat);
|
||||
}
|
||||
mapper.setConfig(config);
|
||||
return mapper;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface SAMLRoleListMapper {
|
||||
|
||||
void mapRoles(AttributeStatementType roleAttributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||
UserSessionModel userSession, ClientSessionModel clientSession);
|
||||
}
|
|
@ -3,7 +3,6 @@ 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.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
|
@ -18,7 +17,7 @@ import java.util.List;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserAttributeBasicAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
|
||||
public class UserAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
|
||||
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
|
||||
|
||||
static {
|
||||
|
@ -28,11 +27,11 @@ public class UserAttributeBasicAttributeStatementMapper extends AbstractSAMLProt
|
|||
property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL);
|
||||
property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
|
||||
configProperties.add(property);
|
||||
AttributeStatementHelper.addBasicProperties(configProperties);
|
||||
AttributeStatementHelper.setConfigProperties(configProperties);
|
||||
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "saml-user-attribute-basic-mapper";
|
||||
public static final String PROVIDER_ID = "saml-user-attribute-mapper";
|
||||
|
||||
|
||||
public List<ConfigProperty> getConfigProperties() {
|
||||
|
@ -45,7 +44,7 @@ public class UserAttributeBasicAttributeStatementMapper extends AbstractSAMLProt
|
|||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "User Attribute Basic";
|
||||
return "User Attribute";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,7 +54,7 @@ public class UserAttributeBasicAttributeStatementMapper extends AbstractSAMLProt
|
|||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Map a custom user attribute to a to a SAML Basic attribute type..";
|
||||
return "Map a custom user attribute to a to a SAML attribute.";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,17 +62,16 @@ public class UserAttributeBasicAttributeStatementMapper extends AbstractSAMLProt
|
|||
UserModel user = userSession.getUser();
|
||||
String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE);
|
||||
String attributeValue = user.getAttribute(attributeName);
|
||||
AttributeStatementHelper.addBasicAttribute(attributeStatement, mappingModel, attributeValue);
|
||||
AttributeStatementHelper.addAttribute(attributeStatement, mappingModel, attributeValue);
|
||||
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel createAttributeMapper(String name,
|
||||
String userAttribute,
|
||||
String samlAttributeName,
|
||||
String friendlyName,
|
||||
public static ProtocolMapperModel createAttributeMapper(String name, String userAttribute,
|
||||
String samlAttributeName, String nameFormat, String friendlyName,
|
||||
boolean consentRequired, String consentText) {
|
||||
String mapperId = PROVIDER_ID;
|
||||
return AttributeStatementHelper.createAttributeMapper(name, userAttribute, samlAttributeName, friendlyName, consentRequired, consentText, mapperId);
|
||||
return AttributeStatementHelper.createAttributeMapper(name, userAttribute, samlAttributeName, nameFormat, friendlyName,
|
||||
consentRequired, consentText, mapperId);
|
||||
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
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.RealmModel;
|
||||
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_ATTRIBUTE);
|
||||
property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL);
|
||||
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_LABEL);
|
||||
String attributeValue = user.getAttribute(attributeName);
|
||||
AttributeStatementHelper.addUriReferenceAttribute(attributeStatement, mappingModel, attributeValue);
|
||||
|
||||
}
|
||||
public static ProtocolMapperModel createAttributeMapper(String name,
|
||||
String userAttribute,
|
||||
String samlAttributeName,
|
||||
String friendlyName,
|
||||
boolean consentRequired, String consentText) {
|
||||
String mapperId = PROVIDER_ID;
|
||||
return AttributeStatementHelper.createAttributeMapper(name, userAttribute, samlAttributeName, friendlyName, consentRequired, consentText, mapperId);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
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.RealmModel;
|
||||
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_ATTRIBUTE);
|
||||
property.setLabel(ProtocolMapperUtils.USER_MODEL_PROPERTY_LABEL);
|
||||
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_LABEL);
|
||||
String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName);
|
||||
AttributeStatementHelper.addBasicAttribute(attributeStatement, mappingModel, propertyValue);
|
||||
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel createAttributeMapper(String name,
|
||||
String userAttribute,
|
||||
String samlAttributeName,
|
||||
String friendlyName,
|
||||
boolean consentRequired, String consentText) {
|
||||
String mapperId = PROVIDER_ID;
|
||||
return AttributeStatementHelper.createAttributeMapper(name, userAttribute, samlAttributeName, friendlyName, consentRequired, consentText, mapperId);
|
||||
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ import java.util.List;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserModelUriReferenceAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
|
||||
public class UserPropertyAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
|
||||
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
|
||||
|
||||
static {
|
||||
|
@ -27,11 +27,11 @@ public class UserModelUriReferenceAttributeStatementMapper extends AbstractSAMLP
|
|||
property.setLabel(ProtocolMapperUtils.USER_MODEL_PROPERTY_LABEL);
|
||||
property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT);
|
||||
configProperties.add(property);
|
||||
AttributeStatementHelper.addUriReferenceProperties(configProperties);
|
||||
AttributeStatementHelper.setConfigProperties(configProperties);
|
||||
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "saml-user-property-uri-mapper";
|
||||
public static final String PROVIDER_ID = "saml-user-property-mapper";
|
||||
|
||||
|
||||
public List<ConfigProperty> getConfigProperties() {
|
||||
|
@ -44,7 +44,7 @@ public class UserModelUriReferenceAttributeStatementMapper extends AbstractSAMLP
|
|||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "User Property URI";
|
||||
return "User Property";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,25 +54,24 @@ public class UserModelUriReferenceAttributeStatementMapper extends AbstractSAMLP
|
|||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Map a built in user property to a SAML URI reference attribute type.";
|
||||
return "Map a built in user property to a SAML 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_LABEL);
|
||||
String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE);
|
||||
String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName);
|
||||
AttributeStatementHelper.addUriReferenceAttribute(attributeStatement, mappingModel, propertyValue);
|
||||
AttributeStatementHelper.addAttribute(attributeStatement, mappingModel, propertyValue);
|
||||
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel createAttributeMapper(String name,
|
||||
String userAttribute,
|
||||
String samlAttributeName,
|
||||
String friendlyName,
|
||||
public static ProtocolMapperModel createAttributeMapper(String name, String userAttribute,
|
||||
String samlAttributeName, String nameFormat, String friendlyName,
|
||||
boolean consentRequired, String consentText) {
|
||||
String mapperId = PROVIDER_ID;
|
||||
return AttributeStatementHelper.createAttributeMapper(name, userAttribute, samlAttributeName, friendlyName, consentRequired, consentText, mapperId);
|
||||
return AttributeStatementHelper.createAttributeMapper(name, userAttribute, samlAttributeName, nameFormat, friendlyName,
|
||||
consentRequired, consentText, mapperId);
|
||||
|
||||
}
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
org.keycloak.protocol.saml.mappers.UserAttributeBasicAttributeStatementMapper
|
||||
org.keycloak.protocol.saml.mappers.UserModelBasicAttributeStatementMapper
|
||||
org.keycloak.protocol.saml.mappers.UserAttributeUriReferenceAttributeStatementMapper
|
||||
org.keycloak.protocol.saml.mappers.UserModelUriReferenceAttributeStatementMapper
|
||||
org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper
|
||||
org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper
|
||||
|
||||
|
||||
|
|
|
@ -18,12 +18,13 @@ public interface ProtocolMapper extends Provider, ProviderFactory<ProtocolMapper
|
|||
public static class ConfigProperty {
|
||||
public static final String BOOLEAN_TYPE="boolean";
|
||||
public static final String STRING_TYPE="String";
|
||||
public static final String LIST_TYPE="List";
|
||||
|
||||
protected String name;
|
||||
protected String label;
|
||||
protected String helpText;
|
||||
protected String type;
|
||||
protected String defaultValue;
|
||||
protected Object defaultValue;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
|
@ -49,11 +50,11 @@ public interface ProtocolMapper extends Provider, ProviderFactory<ProtocolMapper
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
public String getDefaultValue() {
|
||||
public Object getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(String defaultValue) {
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
package org.keycloak.protocol.oidc.mappers;
|
||||
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class OIDCAddClaimMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
|
||||
|
||||
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
|
||||
|
||||
public static final String CLAIM_VALUE = "claim.value";
|
||||
|
||||
static {
|
||||
ConfigProperty property;
|
||||
property = new ConfigProperty();
|
||||
property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
|
||||
property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
|
||||
property.setType(ConfigProperty.STRING_TYPE);
|
||||
property.setHelpText("Claim name you want to hard code 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);
|
||||
property = new ConfigProperty();
|
||||
property.setName(CLAIM_VALUE);
|
||||
property.setLabel("Claim value");
|
||||
property.setType(ConfigProperty.STRING_TYPE);
|
||||
property.setHelpText("Value of the claim you want to hard code. 'true' and 'false can be used for boolean values.");
|
||||
configProperties.add(property);
|
||||
property = new ConfigProperty();
|
||||
property.setName(OIDCAttributeMapperHelper.JSON_TYPE);
|
||||
property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE);
|
||||
property.setType(ConfigProperty.STRING_TYPE);
|
||||
property.setDefaultValue(ConfigProperty.STRING_TYPE);
|
||||
property.setHelpText("JSON type that should be used for the value of the claim. long, int, boolean, and String are valid values.");
|
||||
configProperties.add(property);
|
||||
property = new ConfigProperty();
|
||||
property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
|
||||
property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
|
||||
property.setType(ConfigProperty.BOOLEAN_TYPE);
|
||||
property.setDefaultValue("true");
|
||||
property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
|
||||
configProperties.add(property);
|
||||
property = new ConfigProperty();
|
||||
property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
|
||||
property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
|
||||
property.setType(ConfigProperty.BOOLEAN_TYPE);
|
||||
property.setDefaultValue("true");
|
||||
property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
|
||||
configProperties.add(property);
|
||||
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "oidc-add-claim-mapper";
|
||||
|
||||
|
||||
public List<ConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Hard coded claim";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return TOKEN_MAPPER_CATEGORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Hardcode a claim into the token.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||
UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token;
|
||||
|
||||
setClaim(token, mappingModel, userSession);
|
||||
return token;
|
||||
}
|
||||
|
||||
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
|
||||
String attributeValue = mappingModel.getConfig().get(CLAIM_VALUE);
|
||||
if (attributeValue == null) return;
|
||||
OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attributeValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token;
|
||||
setClaim(token, mappingModel, userSession);
|
||||
return token;
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel createClaimMapper(String name,
|
||||
String userAttribute,
|
||||
String tokenClaimName, String claimType,
|
||||
boolean consentRequired, String consentText,
|
||||
boolean accessToken, boolean idToken) {
|
||||
return OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
|
||||
tokenClaimName, claimType,
|
||||
consentRequired, consentText,
|
||||
accessToken, idToken,
|
||||
PROVIDER_ID);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* Add a role to a token
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class OIDCAddRoleMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
|
||||
|
||||
private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
|
||||
|
||||
public static final String ROLE_CONFIG = "role";
|
||||
|
||||
static {
|
||||
ConfigProperty property;
|
||||
property = new ConfigProperty();
|
||||
property.setName(ROLE_CONFIG);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role you want added to the token. To specify an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "oidc-role-mapper";
|
||||
|
||||
|
||||
public List<ConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Add Role";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return TOKEN_MAPPER_CATEGORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Hardcode any role specify into the token.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||
UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
String role = mappingModel.getConfig().get(ROLE_CONFIG);
|
||||
String appName = null;
|
||||
int scopeIndex = role.indexOf('.');
|
||||
if (scopeIndex > -1) {
|
||||
appName = role.substring(0, scopeIndex);
|
||||
role = role.substring(scopeIndex + 1);
|
||||
token.addAccess(appName).addRole(role);
|
||||
} else {
|
||||
AccessToken.Access access = token.getRealmAccess();
|
||||
if (access == null) {
|
||||
access = new AccessToken.Access();
|
||||
token.setRealmAccess(access);
|
||||
}
|
||||
access.addRole(role);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
|
@ -15,7 +15,8 @@ import java.util.Map;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class OIDCAttributeMapperHelper {
|
||||
public static final String TOKEN_CLAIM_NAME = "Token Claim Name";
|
||||
public static final String TOKEN_CLAIM_NAME = "claim.name";
|
||||
public static final String TOKEN_CLAIM_NAME_LABEL = "Token Claim Name";
|
||||
public static final String JSON_TYPE = "Claim JSON Type";
|
||||
public static final String INCLUDE_IN_ACCESS_TOKEN = "access.token.claim";
|
||||
public static final String INCLUDE_IN_ACCESS_TOKEN_LABEL = "Add to access token";
|
||||
|
|
101
services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCRoleMapper.java
Executable file
101
services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCRoleMapper.java
Executable file
|
@ -0,0 +1,101 @@
|
|||
package org.keycloak.protocol.oidc.mappers;
|
||||
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
||||
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 OIDCRoleMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
|
||||
|
||||
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. The new name format corresponds to where in the access token the role will be mapped to. So, a new name of 'myapp.newname' will map the role to that position in the access token. A new name of 'newname' will map the role to the realm roles in the token.");
|
||||
property.setType(ConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "oidc-role-mapper";
|
||||
|
||||
|
||||
public List<ConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Role Mapper";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return TOKEN_MAPPER_CATEGORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Map an assigned role to a new name or position in the token.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||
UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
String role = mappingModel.getConfig().get(ROLE_CONFIG);
|
||||
String newName = mappingModel.getConfig().get(NEW_ROLE_NAME);
|
||||
String appName = null;
|
||||
int scopeIndex = role.indexOf('.');
|
||||
if (scopeIndex > -1) {
|
||||
appName = role.substring(0, scopeIndex);
|
||||
AccessToken.Access access = token.getResourceAccess(appName);
|
||||
if (access == null) return token;
|
||||
|
||||
role = role.substring(scopeIndex + 1);
|
||||
if (!access.getRoles().contains(role)) return token;
|
||||
access.getRoles().remove(role);
|
||||
} else {
|
||||
AccessToken.Access access = token.getRealmAccess();
|
||||
if (access == null) return token;
|
||||
access.getRoles().remove(role);
|
||||
}
|
||||
|
||||
String newAppName = null;
|
||||
scopeIndex = newName.indexOf('.');
|
||||
if (scopeIndex > -1) {
|
||||
newAppName = role.substring(0, scopeIndex);
|
||||
newName = role.substring(scopeIndex + 1);
|
||||
token.addAccess(newAppName).addRole(newName);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implemen
|
|||
configProperties.add(property);
|
||||
property = new ConfigProperty();
|
||||
property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
|
||||
property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
|
||||
property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
|
||||
property.setType(ConfigProperty.STRING_TYPE);
|
||||
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);
|
||||
|
|
|
@ -34,7 +34,7 @@ public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements O
|
|||
configProperties.add(property);
|
||||
property = new ConfigProperty();
|
||||
property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
|
||||
property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
|
||||
property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
|
||||
property.setType(ConfigProperty.STRING_TYPE);
|
||||
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);
|
||||
|
|
|
@ -2,6 +2,9 @@ org.keycloak.protocol.oidc.mappers.OIDCUserAttributeMapper
|
|||
org.keycloak.protocol.oidc.mappers.OIDCFullNameMapper
|
||||
org.keycloak.protocol.oidc.mappers.OIDCUserModelMapper
|
||||
org.keycloak.protocol.oidc.mappers.OIDCAddressMapper
|
||||
org.keycloak.protocol.oidc.mappers.OIDCAddClaimMapper
|
||||
org.keycloak.protocol.oidc.mappers.OIDCAddRoleMapper
|
||||
org.keycloak.protocol.oidc.mappers.OIDCRoleMapper
|
||||
org.keycloak.protocol.oidc.mappers.OIDCUserSessionNoteMapper
|
||||
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
|
|||
}
|
||||
|
||||
|
||||
private DeploymentInfo createDeploymentInfo(String name, String contextPath, Class<? extends Servlet> servletClass) {
|
||||
public DeploymentInfo createDeploymentInfo(String name, String contextPath, Class<? extends Servlet> servletClass) {
|
||||
DeploymentInfo deploymentInfo = new DeploymentInfo();
|
||||
deploymentInfo.setClassLoader(getClass().getClassLoader());
|
||||
deploymentInfo.setDeploymentName(name);
|
||||
|
|
|
@ -21,7 +21,19 @@ import org.keycloak.testsuite.pages.LoginPage;
|
|||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.picketlink.common.constants.JBossSAMLURIConstants;
|
||||
import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
|
||||
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.assertion.AttributeType;
|
||||
import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
|
||||
import org.picketlink.identity.federation.web.util.PostBindingUtil;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.ClientRequestContext;
|
||||
|
@ -32,6 +44,7 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
|
@ -54,11 +67,15 @@ public class SamlBindingTest {
|
|||
initializeSamlSecuredWar("/saml/signed-post-persistent", "/sales-post-sig-persistent", "post-sig-persistent.war", classLoader);
|
||||
initializeSamlSecuredWar("/saml/signed-metadata", "/sales-metadata", "post-metadata.war", classLoader);
|
||||
initializeSamlSecuredWar("/saml/signed-get", "/employee-sig", "employee-sig.war", classLoader);
|
||||
//initializeSamlSecuredWar("/saml/simple-get", "/employee", "employee.war", classLoader);
|
||||
initializeSamlSecuredWar("/saml/signed-front-get", "/employee-sig-front", "employee-sig-front.war", classLoader);
|
||||
initializeSamlSecuredWar("/saml/bad-client-signed-post", "/bad-client-sales-post-sig", "bad-client-post-sig.war", classLoader);
|
||||
initializeSamlSecuredWar("/saml/bad-realm-signed-post", "/bad-realm-sales-post-sig", "bad-realm-post-sig.war", classLoader);
|
||||
initializeSamlSecuredWar("/saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader);
|
||||
uploadSP();
|
||||
server.getServer().deploy(createDeploymentInfo("employee.war", "/employee", SamlSPFacade.class));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -68,6 +85,30 @@ public class SamlBindingTest {
|
|||
}
|
||||
};
|
||||
|
||||
public static class SamlSPFacade extends HttpServlet {
|
||||
public static String samlResponse;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
handler(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
handler(req, resp);
|
||||
}
|
||||
|
||||
private void handler(HttpServletRequest req, HttpServletResponse resp) {
|
||||
System.out.println("********* HERE ******");
|
||||
if (req.getParameterMap().isEmpty()) {
|
||||
resp.setStatus(302);
|
||||
resp.setHeader("Location", "http://localhost:8081/auth/realms/demo/protocol/saml?SAMLRequest=jVJbT8IwFP4rS99HuwluNIwEIUYSLwugD76Y2h2kSdfOng7l31uGRn0ATfrQ9HznfJfTEYpaN3zS%2Bo1ZwGsL6KP3WhvkXaEgrTPcClTIjagBuZd8Obm55mmP8cZZb6XV5NByGiwQwXllDYkmX9epNdjW4JbgtkrC%2FeK6IBvvG06ptlLojUXPc5YnFOpG2x0AJdEsaFRG7PuPoUWwQx0IXSOtoLb0SynduyLRpXUSOs8FWQuNQKL5rCDz2VO%2FymEgIY2zlJ3H%2FSx9jkU%2BzOK0ys8yNmSSsUEAYxnsqC18tyO2MDfohfEFSVkyiNlZzM5XacrDSbJePug%2Fkqj8FHKhTKXMy%2BnIng8g5FerVRmXd8sViR7AYec8AMh4tPfDO3L3Y2%2F%2F3cT4j7BH9Mf8A1nDb8PA%2Bay0WsldNNHavk1D1D5k4V0LXbi18MclJL2ke1FVvO6gvDXYgFRrBRWh4wPp7z85%2FgA%3D");
|
||||
return;
|
||||
}
|
||||
samlResponse = req.getParameter("SAMLResponse");
|
||||
}
|
||||
}
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
@WebResource
|
||||
|
@ -151,6 +192,52 @@ public class SamlBindingTest {
|
|||
checkLoggedOut("http://localhost:8081/sales-post-sig-email/");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAttributes() throws Exception {
|
||||
// this test has a hardcoded SAMLRequest and we hack a SP face servlet to get the SAMLResponse so we can look
|
||||
// at the assertions sent. This is because Picketlink, AFAICT, does not give you any way to get access to
|
||||
// the assertion.
|
||||
SamlSPFacade.samlResponse = null;
|
||||
driver.navigate().to("http://localhost:8081/employee/");
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
|
||||
System.out.println(driver.getCurrentUrl());
|
||||
loginPage.login("bburke", "password");
|
||||
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee/");
|
||||
Assert.assertNotNull(SamlSPFacade.samlResponse);
|
||||
SAML2Response saml2Response = new SAML2Response();
|
||||
byte[] samlResponse = PostBindingUtil.base64Decode(SamlSPFacade.samlResponse);
|
||||
ResponseType rt = saml2Response.getResponseType(new ByteArrayInputStream(samlResponse));
|
||||
Assert.assertTrue(rt.getAssertions().size() == 1);
|
||||
AssertionType assertion = rt.getAssertions().get(0).getAssertion();
|
||||
|
||||
// test attributes
|
||||
|
||||
boolean email = false;
|
||||
boolean phone = false;
|
||||
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
|
||||
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
|
||||
AttributeType attr = choice.getAttribute();
|
||||
if (X500SAMLProfileConstants.EMAIL.getFriendlyName().equals(attr.getFriendlyName())) {
|
||||
Assert.assertEquals(X500SAMLProfileConstants.EMAIL.get(), attr.getName());
|
||||
Assert.assertEquals(JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(), attr.getNameFormat());
|
||||
Assert.assertEquals(attr.getAttributeValue().get(0), "bburke@redhat.com");
|
||||
email = true;
|
||||
} else if (attr.getName().equals("phone")) {
|
||||
Assert.assertEquals(JBossSAMLURIConstants.ATTRIBUTE_FORMAT_BASIC.get(), attr.getNameFormat());
|
||||
Assert.assertEquals(attr.getAttributeValue().get(0), "617");
|
||||
phone = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Assert.assertTrue(email);
|
||||
Assert.assertTrue(phone);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectSignedLoginLogout() {
|
||||
driver.navigate().to("http://localhost:8081/employee-sig/");
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
class="org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler">
|
||||
|
||||
<Option Key="NAMEID_FORMAT" Value="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"/>
|
||||
<Option Key="ASSERTION_SESSION_ATTRIBUTE_NAME" Value="org.picketlink.sp.assertion"/>
|
||||
|
||||
</Handler>
|
||||
<Handler
|
||||
class="org.picketlink.identity.federation.web.handlers.saml2.RolesGenerationHandler" />
|
||||
|
|
|
@ -24,7 +24,10 @@
|
|||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": ["manager"]
|
||||
"attributes" : {
|
||||
"phone": "617"
|
||||
},
|
||||
"realmRoles": ["manager", "user"]
|
||||
}
|
||||
],
|
||||
"applications": [
|
||||
|
@ -217,6 +220,45 @@
|
|||
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "http://localhost:8081/employee/",
|
||||
"enabled": true,
|
||||
"protocol": "saml",
|
||||
"fullScopeAllowed": true,
|
||||
"baseUrl": "http://localhost:8081/employee",
|
||||
"redirectUris": [
|
||||
"http://localhost:8081/employee/*"
|
||||
],
|
||||
"adminUrl": "http://localhost:8081/employee/",
|
||||
"attributes": {
|
||||
"saml.authnstatement": "true"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"name": "email",
|
||||
"protocol": "saml",
|
||||
"protocolMapper": "saml-user-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "email",
|
||||
"friendly.name": "email",
|
||||
"attribute.name": "urn:oid:1.2.840.113549.1.9.1",
|
||||
"attribute.nameformat": "URI Reference"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "phone",
|
||||
"protocol": "saml",
|
||||
"protocolMapper": "saml-user-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "phone",
|
||||
"attribute.name": "phone",
|
||||
"attribute.nameformat": "Basic"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "http://localhost:8081/employee-sig-front/",
|
||||
"enabled": true,
|
||||
|
@ -246,6 +288,10 @@
|
|||
{
|
||||
"name": "manager",
|
||||
"description": "Have Manager privileges"
|
||||
},
|
||||
{
|
||||
"name": "user",
|
||||
"description": "Have User privileges"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue