keycloak-aplcache
Changes
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-protocol-mapper-detail.html 7(+6 -1)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/protocol-mapper-detail.html 9(+7 -2)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java 73(+35 -38)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/SAMLRoleListMapper.java 17(+17 -0)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java 24(+11 -13)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserModelBasicAttributeStatementMapper.java 79(+0 -79)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java 23(+11 -12)
Details
diff --git a/core/src/main/java/org/keycloak/representations/AccessToken.java b/core/src/main/java/org/keycloak/representations/AccessToken.java
index 4d099ca..81d3615 100755
--- a/core/src/main/java/org/keycloak/representations/AccessToken.java
+++ b/core/src/main/java/org/keycloak/representations/AccessToken.java
@@ -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) {
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 1e510f8..779f3be 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 a98bbe7..36738b6 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 @@
<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>
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 25be1f1..aed7f50 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 @@
<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>
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 3bbb7fc..378a0f6 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 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);
+ 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(SAML_ATTRIBUTE_NAME);
- property.setLabel(URI_REFERENCE_LABEL);
- property.setHelpText(URI_REFERENCE_HELP_TEXT);
+ property.setName(AttributeStatementHelper.SAML_ATTRIBUTE_NAME);
+ property.setLabel("SAML Attribute Name");
+ property.setHelpText("SAML Attribute Name");
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);
+ property.setName(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT);
+ property.setLabel("SAML Attribute NameFormat");
+ property.setHelpText("SAML Attribute NameFormat. Can be basic, URI reference, or unspecified.");
+ List<String> types = new ArrayList(3);
+ types.add(AttributeStatementHelper.BASIC);
+ types.add(AttributeStatementHelper.URI_REFERENCE);
+ types.add(AttributeStatementHelper.UNSPECIFIED);
+ property.setType(ProtocolMapper.ConfigProperty.LIST_TYPE);
+ property.setDefaultValue(types);
configProperties.add(property);
- property = new 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/SAMLRoleListMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/SAMLRoleListMapper.java
new file mode 100755
index 0000000..b2d500f
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/SAMLRoleListMapper.java
@@ -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);
+}
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 934abe9..5f673fb 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 <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);
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 719ddff..7d75839 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/oidc/mappers/OIDCAddClaimMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddClaimMapper.java
new file mode 100755
index 0000000..fad7bd6
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddClaimMapper.java
@@ -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);
+ }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
index 8819713..b91c233 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
@@ -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";
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCRoleMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCRoleMapper.java
new file mode 100755
index 0000000..3ed9b61
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCRoleMapper.java
@@ -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;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java
index f0f5657..b2ea92d 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java
@@ -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);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java
index cd0cd03..3b559e2 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java
@@ -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);
diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java
index ff6e105..d986dec 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, 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;
}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
index 137b29a..f8cc934 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
+++ b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
@@ -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
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index b00bb73..15dfca3 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -1,158 +1,158 @@
-/*
- * 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 <a href="mailto:bburke@redhat.com">Bill Burke</a>
- */
-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 <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+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 169ff3a..d1e37b3 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<? 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);
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 db33eb1..8f8f2b4 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 1d170ee..7636260 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">
<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" />
diff --git a/testsuite/integration/src/test/resources/saml/testsaml.json b/testsuite/integration/src/test/resources/saml/testsaml.json
index 9635c0c..0baffb5 100755
--- a/testsuite/integration/src/test/resources/saml/testsaml.json
+++ b/testsuite/integration/src/test/resources/saml/testsaml.json
@@ -24,7 +24,10 @@
{ "type" : "password",
"value" : "password" }
],
- "realmRoles": ["manager"]
+ "attributes" : {
+ "phone": "617"
+ },
+ "realmRoles": ["manager", "user"]
}
],
"applications": [
@@ -218,6 +221,45 @@
}
},
{
+ "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,
"protocol": "saml",
@@ -246,6 +288,10 @@
{
"name": "manager",
"description": "Have Manager privileges"
+ },
+ {
+ "name": "user",
+ "description": "Have User privileges"
}
]
}