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/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/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/OIDCAddRoleMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddRoleMapper.java
new file mode 100755
index 0000000..c46b9d4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddRoleMapper.java
@@ -0,0 +1,81 @@
+package org.keycloak.protocol.oidc.mappers;
+
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.representations.AccessToken;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Add a role to a token
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCAddRoleMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
+
+ private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+
+ public static final String ROLE_CONFIG = "role";
+
+ static {
+ ConfigProperty property;
+ property = new ConfigProperty();
+ property.setName(ROLE_CONFIG);
+ property.setLabel("Role");
+ property.setHelpText("Role you want added to the token. To specify an application role the syntax is appname.approle, i.e. myapp.myrole");
+ property.setType(ConfigProperty.STRING_TYPE);
+ configProperties.add(property);
+ }
+
+ public static final String PROVIDER_ID = "oidc-role-mapper";
+
+
+ public List<ConfigProperty> getConfigProperties() {
+ return configProperties;
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Add Role";
+ }
+
+ @Override
+ public String getDisplayCategory() {
+ return TOKEN_MAPPER_CATEGORY;
+ }
+
+ @Override
+ public String getHelpText() {
+ return "Hardcode any role specify into the token.";
+ }
+
+ @Override
+ public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
+ UserSessionModel userSession, ClientSessionModel clientSession) {
+ String role = mappingModel.getConfig().get(ROLE_CONFIG);
+ String appName = null;
+ int scopeIndex = role.indexOf('.');
+ if (scopeIndex > -1) {
+ appName = role.substring(0, scopeIndex);
+ role = role.substring(scopeIndex + 1);
+ token.addAccess(appName).addRole(role);
+ } else {
+ AccessToken.Access access = token.getRealmAccess();
+ if (access == null) {
+ access = new AccessToken.Access();
+ token.setRealmAccess(access);
+ }
+ access.addRole(role);
+ }
+ return token;
+ }
+}
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..10724ef
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCRoleMapper.java
@@ -0,0 +1,100 @@
+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.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/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
index 1615ecc..d89c1bf 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,5 +2,8 @@ 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