diff --git a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java index 1e510f8bfa..779f3be519 100755 --- a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java @@ -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; } diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-protocol-mapper-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-protocol-mapper-detail.html index a98bbe7cf6..36738b65b6 100755 --- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-protocol-mapper-detail.html +++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-protocol-mapper-detail.html @@ -78,12 +78,17 @@
-
+
+
+ +
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/protocol-mapper-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/protocol-mapper-detail.html index 25be1f11d0..aed7f506c6 100755 --- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/protocol-mapper-detail.html +++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/protocol-mapper-detail.html @@ -76,14 +76,19 @@
- + -
+
+
+ +
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java index 934abe90f1..5f673fbdb9 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java @@ -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 Bill Burke @@ -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); diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java index 3bbb7fcb75..378a0f613d 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java @@ -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 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 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 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 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; } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeBasicAttributeStatementMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java similarity index 70% rename from saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeBasicAttributeStatementMapper.java rename to saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java index 16374dd991..fd83512334 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeBasicAttributeStatementMapper.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java @@ -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 Bill Burke * @version $Revision: 1 $ */ -public class UserAttributeBasicAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper { +public class UserAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper { private static final List configProperties = new ArrayList(); 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 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, - boolean consentRequired, String consentText) { + 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); } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeUriReferenceAttributeStatementMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeUriReferenceAttributeStatementMapper.java deleted file mode 100755 index 232474dbc0..0000000000 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeUriReferenceAttributeStatementMapper.java +++ /dev/null @@ -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 Bill Burke - * @version $Revision: 1 $ - */ -public class UserAttributeUriReferenceAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper { - private static final List configProperties = new ArrayList(); - - 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 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); - - } - -} diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserModelBasicAttributeStatementMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserModelBasicAttributeStatementMapper.java deleted file mode 100755 index 35b3354ba9..0000000000 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserModelBasicAttributeStatementMapper.java +++ /dev/null @@ -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 Bill Burke - * @version $Revision: 1 $ - */ -public class UserModelBasicAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper { - private static final List configProperties = new ArrayList(); - - 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 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); - - } -} diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserModelUriReferenceAttributeStatementMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java similarity index 72% rename from saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserModelUriReferenceAttributeStatementMapper.java rename to saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java index 8037638e46..6294b68000 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserModelUriReferenceAttributeStatementMapper.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java @@ -17,7 +17,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class UserModelUriReferenceAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper { +public class UserPropertyAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper { private static final List configProperties = new ArrayList(); 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 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); } } diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper index 719ddff57d..7d75839140 100755 --- a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper +++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper @@ -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 diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java index ff6e10591c..d986decd59 100755 --- a/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java +++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java @@ -18,12 +18,13 @@ public interface ProtocolMapper extends Provider, ProviderFactoryBill Burke - */ -public class AdapterTest { - - public static PublicKey realmPublicKey; - @ClassRule - public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() { - @Override - protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) { - RealmModel realm = AdapterTestStrategy.baseAdapterTestInitialization(session, manager, adminRealm, getClass()); - realmPublicKey = realm.getPublicKey(); - - URL url = getClass().getResource("/adapter-test/cust-app-keycloak.json"); - deployApplication("customer-portal", "/customer-portal", CustomerServlet.class, url.getPath(), "user"); - url = getClass().getResource("/adapter-test/secure-portal-keycloak.json"); - deployApplication("secure-portal", "/secure-portal", CallAuthenticatedServlet.class, url.getPath(), "user", false); - url = getClass().getResource("/adapter-test/customer-db-keycloak.json"); - deployApplication("customer-db", "/customer-db", CustomerDatabaseServlet.class, url.getPath(), "user"); - url = getClass().getResource("/adapter-test/product-keycloak.json"); - deployApplication("product-portal", "/product-portal", ProductServlet.class, url.getPath(), "user"); - - // Test that replacing system properties works for adapters - System.setProperty("app.server.base.url", "http://localhost:8081"); - System.setProperty("my.host.name", "localhost"); - url = getClass().getResource("/adapter-test/session-keycloak.json"); - deployApplication("session-portal", "/session-portal", SessionServlet.class, url.getPath(), "user"); - url = getClass().getResource("/adapter-test/input-keycloak.json"); - deployApplication("input-portal", "/input-portal", InputServlet.class, url.getPath(), "user", true, null, "/secured/*"); - } - }; - - @Rule - public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8081", keycloakRule); - - @Test - public void testLoginSSOAndLogout() throws Exception { - testStrategy.testLoginSSOAndLogout(); - } - - @Test - public void testSavedPostRequest() throws Exception { - testStrategy.testSavedPostRequest(); - } - - @Test - public void testServletRequestLogout() throws Exception { - testStrategy.testServletRequestLogout(); - } - - @Test - public void testLoginSSOIdle() throws Exception { - testStrategy.testLoginSSOIdle(); - - } - - @Test - public void testLoginSSOIdleRemoveExpiredUserSessions() throws Exception { - testStrategy.testLoginSSOIdleRemoveExpiredUserSessions(); - } - - @Test - public void testLoginSSOMax() throws Exception { - testStrategy.testLoginSSOMax(); - } - - /** - * KEYCLOAK-518 - * @throws Exception - */ - @Test - public void testNullBearerToken() throws Exception { - testStrategy.testNullBearerToken(); - } - - /** - * KEYCLOAK-518 - * @throws Exception - */ - @Test - public void testBadUser() throws Exception { - testStrategy.testBadUser(); - } - - @Test - public void testVersion() throws Exception { - testStrategy.testVersion(); - } - - @Test - public void testAuthenticated() throws Exception { - testStrategy.testAuthenticated(); - } - - /** - * KEYCLOAK-732 - * - * @throws Throwable - */ - @Test - public void testSingleSessionInvalidated() throws Throwable { - testStrategy.testSingleSessionInvalidated(); - } - - /** - * KEYCLOAK-741 - */ - @Test - public void testSessionInvalidatedAfterFailedRefresh() throws Throwable { - testStrategy.testSessionInvalidatedAfterFailedRefresh(); - - } - - /** - * KEYCLOAK-942 - */ - @Test - public void testAdminApplicationLogout() throws Throwable { - testStrategy.testAdminApplicationLogout(); - } - -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2012, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.keycloak.testsuite.adapter; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.testsuite.rule.AbstractKeycloakRule; + +import java.net.URL; +import java.security.PublicKey; + +/** + * Tests Undertow Adapter + * + * @author Bill Burke + */ +public class AdapterTest { + + public static PublicKey realmPublicKey; + @ClassRule + public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() { + @Override + protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) { + RealmModel realm = AdapterTestStrategy.baseAdapterTestInitialization(session, manager, adminRealm, getClass()); + realmPublicKey = realm.getPublicKey(); + + URL url = getClass().getResource("/adapter-test/cust-app-keycloak.json"); + deployApplication("customer-portal", "/customer-portal", CustomerServlet.class, url.getPath(), "user"); + url = getClass().getResource("/adapter-test/secure-portal-keycloak.json"); + deployApplication("secure-portal", "/secure-portal", CallAuthenticatedServlet.class, url.getPath(), "user", false); + url = getClass().getResource("/adapter-test/customer-db-keycloak.json"); + deployApplication("customer-db", "/customer-db", CustomerDatabaseServlet.class, url.getPath(), "user"); + url = getClass().getResource("/adapter-test/product-keycloak.json"); + deployApplication("product-portal", "/product-portal", ProductServlet.class, url.getPath(), "user"); + + // Test that replacing system properties works for adapters + System.setProperty("app.server.base.url", "http://localhost:8081"); + System.setProperty("my.host.name", "localhost"); + url = getClass().getResource("/adapter-test/session-keycloak.json"); + deployApplication("session-portal", "/session-portal", SessionServlet.class, url.getPath(), "user"); + url = getClass().getResource("/adapter-test/input-keycloak.json"); + deployApplication("input-portal", "/input-portal", InputServlet.class, url.getPath(), "user", true, null, "/secured/*"); + } + }; + + @Rule + public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8081", keycloakRule); + + @Test + public void testLoginSSOAndLogout() throws Exception { + testStrategy.testLoginSSOAndLogout(); + } + + @Test + public void testSavedPostRequest() throws Exception { + testStrategy.testSavedPostRequest(); + } + + @Test + public void testServletRequestLogout() throws Exception { + testStrategy.testServletRequestLogout(); + } + + @Test + public void testLoginSSOIdle() throws Exception { + testStrategy.testLoginSSOIdle(); + + } + + @Test + public void testLoginSSOIdleRemoveExpiredUserSessions() throws Exception { + testStrategy.testLoginSSOIdleRemoveExpiredUserSessions(); + } + + @Test + public void testLoginSSOMax() throws Exception { + testStrategy.testLoginSSOMax(); + } + + /** + * KEYCLOAK-518 + * @throws Exception + */ + @Test + public void testNullBearerToken() throws Exception { + testStrategy.testNullBearerToken(); + } + + /** + * KEYCLOAK-518 + * @throws Exception + */ + @Test + public void testBadUser() throws Exception { + testStrategy.testBadUser(); + } + + @Test + public void testVersion() throws Exception { + testStrategy.testVersion(); + } + + @Test + public void testAuthenticated() throws Exception { + testStrategy.testAuthenticated(); + } + + /** + * KEYCLOAK-732 + * + * @throws Throwable + */ + @Test + public void testSingleSessionInvalidated() throws Throwable { + testStrategy.testSingleSessionInvalidated(); + } + + /** + * KEYCLOAK-741 + */ + @Test + public void testSessionInvalidatedAfterFailedRefresh() throws Throwable { + testStrategy.testSessionInvalidatedAfterFailedRefresh(); + + } + + /** + * KEYCLOAK-942 + */ + @Test + public void testAdminApplicationLogout() throws Throwable { + testStrategy.testAdminApplicationLogout(); + } + +} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java index 169ff3a88e..d1e37b3e17 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java @@ -128,7 +128,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource { } - private DeploymentInfo createDeploymentInfo(String name, String contextPath, Class servletClass) { + public DeploymentInfo createDeploymentInfo(String name, String contextPath, Class servletClass) { DeploymentInfo deploymentInfo = new DeploymentInfo(); deploymentInfo.setClassLoader(getClass().getClassLoader()); deploymentInfo.setDeploymentName(name); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java index db33eb1340..8f8f2b492f 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java @@ -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/"); diff --git a/testsuite/integration/src/test/resources/saml/simple-get/WEB-INF/picketlink.xml b/testsuite/integration/src/test/resources/saml/simple-get/WEB-INF/picketlink.xml index 1d170ee3a1..7636260689 100755 --- a/testsuite/integration/src/test/resources/saml/simple-get/WEB-INF/picketlink.xml +++ b/testsuite/integration/src/test/resources/saml/simple-get/WEB-INF/picketlink.xml @@ -12,8 +12,6 @@ class="org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler">