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 83b4b1a488..8ef3c80c0c 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 @@ -9,7 +9,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.protocol.AbstractLoginProtocolFactory; import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.saml.mappers.AttributeStatementHelper; -import org.keycloak.protocol.saml.mappers.SAMLBasicRoleListMapper; +import org.keycloak.protocol.saml.mappers.RoleListMapper; import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper; import org.keycloak.services.managers.AuthenticationManager; import org.picketlink.common.constants.JBossSAMLURIConstants; @@ -77,7 +77,7 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory { X500SAMLProfileConstants.SURNAME.getFriendlyName(), true, "family name"); builtins.add(model); - model = SAMLBasicRoleListMapper.create("role list", "Role", AttributeStatementHelper.BASIC, null, false); + model = RoleListMapper.create("role list", "Role", AttributeStatementHelper.BASIC, null, false); builtins.add(model); defaultBuiltins.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 b5850a5bce..06b299ad49 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 @@ -73,14 +73,14 @@ public class AttributeStatementHelper { } 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(); + ProtocolMapperModel mapper = new ProtocolMapperModel(); mapper.setName(name); mapper.setProtocolMapper(mapperId); mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL); mapper.setConsentRequired(consentRequired); mapper.setConsentText(consentText); Map config = new HashMap(); - config.put(ProtocolMapperUtils.USER_ATTRIBUTE, userAttribute); + if (userAttribute != null) config.put(ProtocolMapperUtils.USER_ATTRIBUTE, userAttribute); config.put(SAML_ATTRIBUTE_NAME, samlAttributeName); if (friendlyName != null) { config.put(FRIENDLY_NAME, friendlyName); diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java new file mode 100755 index 0000000000..221bcfd273 --- /dev/null +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java @@ -0,0 +1,85 @@ +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.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 HardcodedAttributeMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper { + public static final String PROVIDER_ID = "saml-hardcode-attribute-mapper"; + public static final String ATTRIBUTE_VALUE = "attribute.value"; + 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.setConfigProperties(configProperties); + property = new ConfigProperty(); + property.setName(ATTRIBUTE_VALUE); + property.setLabel("Attribute value"); + property.setType(ConfigProperty.STRING_TYPE); + property.setHelpText("Value of the attribute you want to hard code."); + configProperties.add(property); + + } + + + + public List getConfigProperties() { + return configProperties; + } + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public String getDisplayType() { + return "Hardcoded attribute"; + } + + @Override + public String getDisplayCategory() { + return AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY; + } + + @Override + public String getHelpText() { + return "Hardcode an attribute into the SAML Assertion."; + } + + @Override + public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { + String attributeValue = mappingModel.getConfig().get(ATTRIBUTE_VALUE); + AttributeStatementHelper.addAttribute(attributeStatement, mappingModel, attributeValue); + + } + + public static ProtocolMapperModel create(String name, + String samlAttributeName, String nameFormat, String friendlyName, String value, + boolean consentRequired, String consentText) { + String mapperId = PROVIDER_ID; + ProtocolMapperModel model = AttributeStatementHelper.createAttributeMapper(name, null, samlAttributeName, nameFormat, friendlyName, + consentRequired, consentText, mapperId); + model.getConfig().put(ATTRIBUTE_VALUE, value); + return model; + + } + +} diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedRole.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedRole.java new file mode 100755 index 0000000000..862a0ca28e --- /dev/null +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedRole.java @@ -0,0 +1,76 @@ +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.keycloak.protocol.ProtocolMapperUtils; +import org.keycloak.protocol.saml.SamlProtocol; +import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Mappings UserModel property (the property name of a getter method) to an AttributeStatement. + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class HardcodedRole extends AbstractSAMLProtocolMapper { + public static final String PROVIDER_ID = "saml-hardcode-role-mapper"; + public static final String ATTRIBUTE_VALUE = "attribute.value"; + private static final List configProperties = new ArrayList(); + + static { + ConfigProperty property; + property = new ConfigProperty(); + property.setName("role"); + property.setLabel("Role"); + property.setHelpText("Role name you want to hardcode."); + property.setType(ConfigProperty.STRING_TYPE); + configProperties.add(property); + } + + + + public List getConfigProperties() { + return configProperties; + } + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public String getDisplayType() { + return "Hardcoded role"; + } + + @Override + public String getDisplayCategory() { + return AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY; + } + + @Override + public String getHelpText() { + return "Hardcode role into SAML Assertion."; + } + + public static ProtocolMapperModel create(String name, + String role) { + String mapperId = PROVIDER_ID; + ProtocolMapperModel mapper = new ProtocolMapperModel(); + mapper.setName(name); + mapper.setProtocolMapper(mapperId); + mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + Map config = new HashMap(); + config.put("role", role); + mapper.setConfig(config); + return mapper; + + } + +} diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/SAMLBasicRoleListMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java similarity index 79% rename from saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/SAMLBasicRoleListMapper.java rename to saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java index 9895944a41..68188cf80f 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/SAMLBasicRoleListMapper.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java @@ -14,6 +14,7 @@ import org.picketlink.identity.federation.saml.v2.assertion.AttributeType; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -21,7 +22,7 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class SAMLBasicRoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRoleListMapper { +public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRoleListMapper { public static final String PROVIDER_ID = "saml-role-list-mapper"; public static final String SINGLE_ROLE_ATTRIBUTE = "single"; @@ -92,17 +93,33 @@ public class SAMLBasicRoleListMapper extends AbstractSAMLProtocolMapper implemen String single = mappingModel.getConfig().get(SINGLE_ROLE_ATTRIBUTE); boolean singleAttribute = Boolean.parseBoolean(single); - Map roleNameMappers = new HashMap<>(); + List> roleNameMappers = new LinkedList<>(); KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory(); + AttributeType singleAttributeType = null; for (ProtocolMapperModel mapping : clientSession.getClient().getProtocolMappers()) { if (!mapping.getProtocol().equals(SamlProtocol.LOGIN_PROTOCOL)) continue; ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper()); - if (mapper == null || !(mapper instanceof SAMLRoleNameMapper)) continue; - roleNameMappers.put(mapping, (SAMLRoleNameMapper)mapper); + if (mapper == null) continue; + if (mapper instanceof SAMLRoleNameMapper) { + roleNameMappers.add(new SamlProtocol.ProtocolMapperProcessor<>((SAMLRoleNameMapper) mapper,mapping)); + } + if (mapper instanceof HardcodedRole) { + AttributeType attributeType = null; + if (singleAttribute) { + if (singleAttributeType == null) { + singleAttributeType = AttributeStatementHelper.createAttributeType(mappingModel); + roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(singleAttributeType)); + } + attributeType = singleAttributeType; + } else { + attributeType = AttributeStatementHelper.createAttributeType(mappingModel); + roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType)); + } + attributeType.addAttributeValue(mapping.getConfig().get("role")); + } } - AttributeType singleAttributeType = null; for (String roleId : clientSession.getRoles()) { // todo need a role mapping RoleModel roleModel = clientSession.getRealm().getRoleById(roleId); @@ -118,8 +135,8 @@ public class SAMLBasicRoleListMapper extends AbstractSAMLProtocolMapper implemen roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType)); } String roleName = roleModel.getName(); - for (Map.Entry entry : roleNameMappers.entrySet()) { - String newName = entry.getValue().mapName(entry.getKey(), roleModel); + for (SamlProtocol.ProtocolMapperProcessor entry : roleNameMappers) { + String newName = entry.mapper.mapName(entry.model, roleModel); if (newName != null) { roleName = newName; break; diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/SAMLBasicRoleNameMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleNameMapper.java similarity index 75% rename from saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/SAMLBasicRoleNameMapper.java rename to saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleNameMapper.java index b56cf1f4f8..adde0b8580 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/SAMLBasicRoleNameMapper.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleNameMapper.java @@ -5,9 +5,12 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper; +import org.keycloak.protocol.saml.SamlProtocol; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Map an assigned role to a different position and name in the token @@ -15,7 +18,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class SAMLBasicRoleNameMapper extends AbstractOIDCProtocolMapper implements SAMLRoleNameMapper { +public class RoleNameMapper extends AbstractOIDCProtocolMapper implements SAMLRoleNameMapper { private static final List configProperties = new ArrayList(); @@ -88,4 +91,21 @@ public class SAMLBasicRoleNameMapper extends AbstractOIDCProtocolMapper implemen if (roleModel.getName().equals(role)) return newName; return null; } + + public static ProtocolMapperModel create(String name, + String role, + String newName) { + String mapperId = PROVIDER_ID; + ProtocolMapperModel mapper = new ProtocolMapperModel(); + mapper.setName(name); + mapper.setProtocolMapper(mapperId); + mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + Map config = new HashMap(); + config.put(ROLE_CONFIG, role); + config.put(NEW_ROLE_NAME, newName); + mapper.setConfig(config); + return mapper; + + } + } 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 62f8079999..392feae64e 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,5 +1,7 @@ -org.keycloak.protocol.saml.mappers.SAMLBasicRoleListMapper -org.keycloak.protocol.saml.mappers.SAMLBasicRoleNameMapper +org.keycloak.protocol.saml.mappers.RoleListMapper +org.keycloak.protocol.saml.mappers.RoleNameMapper +org.keycloak.protocol.saml.mappers.HardcodedRole +org.keycloak.protocol.saml.mappers.HardcodedAttributeMapper org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java index 1c27033383..120446f88a 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java @@ -8,11 +8,11 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.protocol.AbstractLoginProtocolFactory; import org.keycloak.protocol.LoginProtocol; -import org.keycloak.protocol.oidc.mappers.OIDCAddressMapper; +import org.keycloak.protocol.oidc.mappers.AddressMapper; +import org.keycloak.protocol.oidc.mappers.FullNameMapper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; -import org.keycloak.protocol.oidc.mappers.OIDCFullNameMapper; -import org.keycloak.protocol.oidc.mappers.OIDCUserModelMapper; -import org.keycloak.protocol.oidc.mappers.OIDCUserSessionNoteMapper; +import org.keycloak.protocol.oidc.mappers.UserPropertyMapper; +import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; import org.keycloak.services.managers.AuthenticationManager; import java.util.ArrayList; @@ -41,35 +41,35 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory { static { ProtocolMapperModel model; - model = OIDCUserModelMapper.createClaimMapper("username", + model = UserPropertyMapper.createClaimMapper("username", "username", "preferred_username", "String", true, "username", true, true); builtins.add(model); defaultBuiltins.add(model); - model = OIDCUserModelMapper.createClaimMapper("email", + model = UserPropertyMapper.createClaimMapper("email", "email", "email", "String", true, "email", true, true); builtins.add(model); defaultBuiltins.add(model); - model = OIDCUserModelMapper.createClaimMapper("given name", + model = UserPropertyMapper.createClaimMapper("given name", "firstName", "given_name", "String", true, "given name", true, true); builtins.add(model); defaultBuiltins.add(model); - model = OIDCUserModelMapper.createClaimMapper("family name", + model = UserPropertyMapper.createClaimMapper("family name", "lastName", "family_name", "String", true, "family name", true, true); builtins.add(model); defaultBuiltins.add(model); - model = OIDCUserModelMapper.createClaimMapper("email verified", + model = UserPropertyMapper.createClaimMapper("email verified", "emailVerified", "email_verified", "boolean", false, null, @@ -78,7 +78,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory { ProtocolMapperModel fullName = new ProtocolMapperModel(); fullName.setName("full name"); - fullName.setProtocolMapper(OIDCFullNameMapper.PROVIDER_ID); + fullName.setProtocolMapper(FullNameMapper.PROVIDER_ID); fullName.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); fullName.setConsentRequired(true); fullName.setConsentText("full name"); @@ -89,10 +89,10 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory { builtins.add(fullName); defaultBuiltins.add(fullName); - ProtocolMapperModel address = OIDCAddressMapper.createAddressMapper(); + ProtocolMapperModel address = AddressMapper.createAddressMapper(); builtins.add(address); - model = OIDCUserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, + model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, KerberosConstants.GSS_DELEGATION_CREDENTIAL, KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String", true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java index 5bcc03130e..3de9a8716a 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java @@ -30,6 +30,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.OAuthClientModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RequiredCredentialModel; +import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionProvider; @@ -372,85 +373,22 @@ public class OIDCLoginProtocolService { Map err = new HashMap(); err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_GRANT); err.put(OAuth2Constants.ERROR_DESCRIPTION, "Token invalid"); + logger.error("Invalid token. Token verification failed."); event.error(Errors.INVALID_TOKEN); return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(err) .build(); } event.user(token.getSubject()).session(token.getSessionState()).detail(Details.VALIDATE_ACCESS_TOKEN, token.getId()); - if (token.isExpired() - || token.getIssuedAt() < realm.getNotBefore() - ) { - Map err = new HashMap(); - err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_GRANT); - err.put(OAuth2Constants.ERROR_DESCRIPTION, "Token expired"); - event.error(Errors.INVALID_TOKEN); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(err) - .build(); - } - - - UserModel user = session.users().getUserById(token.getSubject(), realm); - if (user == null) { - Map err = new HashMap(); - err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_GRANT); - err.put(OAuth2Constants.ERROR_DESCRIPTION, "User does not exist"); - event.error(Errors.USER_NOT_FOUND); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(err) - .build(); - } - - if (!user.isEnabled()) { - Map err = new HashMap(); - err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_GRANT); - err.put(OAuth2Constants.ERROR_DESCRIPTION, "User disabled"); - event.error(Errors.USER_DISABLED); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(err) - .build(); - } - - UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState()); - if (!AuthenticationManager.isSessionValid(realm, userSession)) { - Map err = new HashMap(); - err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_GRANT); - err.put(OAuth2Constants.ERROR_DESCRIPTION, "Expired session"); - event.error(Errors.USER_SESSION_NOT_FOUND); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(err) - .build(); - } - - ClientModel client = realm.findClient(token.getIssuedFor()); - if (client == null) { - Map err = new HashMap(); - err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_CLIENT); - err.put(OAuth2Constants.ERROR_DESCRIPTION, "Issued for client no longer exists"); - event.error(Errors.CLIENT_NOT_FOUND); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(err) - .build(); - - } - - if (token.getIssuedAt() < client.getNotBefore()) { - Map err = new HashMap(); - err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_CLIENT); - err.put(OAuth2Constants.ERROR_DESCRIPTION, "Issued for client no longer exists"); - event.error(Errors.INVALID_TOKEN); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(err) - .build(); - } - try { - tokenManager.verifyAccess(token, realm, client, user); + tokenManager.validateToken(session, uriInfo, clientConnection, realm, token); } catch (OAuthErrorException e) { - Map err = new HashMap(); - err.put(OAuth2Constants.ERROR, OAuthErrorException.INVALID_SCOPE); - err.put(OAuth2Constants.ERROR_DESCRIPTION, "Role mappings have changed"); + Map error = new HashMap(); + error.put(OAuth2Constants.ERROR, e.getError()); + if (e.getDescription() != null) error.put(OAuth2Constants.ERROR_DESCRIPTION, e.getDescription()); event.error(Errors.INVALID_TOKEN); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(err) - .build(); - + return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); } - event.success(); return Response.ok(token, MediaType.APPLICATION_JSON_TYPE).build(); @@ -666,16 +604,6 @@ public class OIDCLoginProtocolService { AccessToken token = tokenManager.createClientAccessToken(session, accessCode.getRequestedRoles(), realm, client, user, userSession, clientSession); - try { - tokenManager.verifyAccess(token, realm, client, user); - } catch (OAuthErrorException e) { - Map error = new HashMap(); - error.put(OAuth2Constants.ERROR, e.getError()); - if (e.getDescription() != null) error.put(OAuth2Constants.ERROR_DESCRIPTION, e.getDescription()); - event.error(Errors.INVALID_CODE); - return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); - } - AccessTokenResponse res = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSession) .accessToken(token) .generateIDToken() diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java index 14fb6ec32f..07fd828c59 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -59,12 +59,22 @@ public class TokenManager { } } - public AccessTokenResponse refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel client, String encodedRefreshToken, EventBuilder event) throws OAuthErrorException { - RefreshToken refreshToken = verifyRefreshToken(realm, encodedRefreshToken); + public static class TokenValidation { + public final UserModel user; + public final UserSessionModel userSession; + public final ClientSessionModel clientSession; + public final AccessToken newToken; - event.user(refreshToken.getSubject()).session(refreshToken.getSessionState()).detail(Details.REFRESH_TOKEN_ID, refreshToken.getId()); + public TokenValidation(UserModel user, UserSessionModel userSession, ClientSessionModel clientSession, AccessToken newToken) { + this.user = user; + this.userSession = userSession; + this.clientSession = clientSession; + this.newToken = newToken; + } + } - UserModel user = session.users().getUserById(refreshToken.getSubject(), realm); + public TokenValidation validateToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, AccessToken oldToken) throws OAuthErrorException { + UserModel user = session.users().getUserById(oldToken.getSubject(), realm); if (user == null) { throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown user"); } @@ -73,24 +83,14 @@ public class TokenManager { throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "User disabled", "User disabled"); } - UserSessionModel userSession = session.sessions().getUserSession(realm, refreshToken.getSessionState()); - int currentTime = Time.currentTime(); + UserSessionModel userSession = session.sessions().getUserSession(realm, oldToken.getSessionState()); if (!AuthenticationManager.isSessionValid(realm, userSession)) { AuthenticationManager.logout(session, realm, userSession, uriInfo, connection); throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active"); } - - if (!client.getClientId().equals(refreshToken.getIssuedFor())) { - throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Unmatching clients", "Unmatching clients"); - } - - if (refreshToken.getIssuedAt() < client.getNotBefore()) { - throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale refresh token"); - } - ClientSessionModel clientSession = null; for (ClientSessionModel clientSessionModel : userSession.getClientSessions()) { - if (clientSessionModel.getId().equals(refreshToken.getClientSession())) { + if (clientSessionModel.getId().equals(oldToken.getClientSession())) { clientSession = clientSessionModel; break; } @@ -98,20 +98,48 @@ public class TokenManager { if (clientSession == null) { throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Client session not active", "Client session not active"); - } - verifyAccess(refreshToken, realm, client, user); + ClientModel client = clientSession.getClient(); - AccessToken accessToken = initToken(realm, client, user, userSession, clientSession); - accessToken.setRealmAccess(refreshToken.getRealmAccess()); - accessToken.setResourceAccess(refreshToken.getResourceAccess()); - accessToken = transformAccessToken(session, accessToken, realm, client, user, userSession, clientSession); + if (!client.getClientId().equals(oldToken.getIssuedFor())) { + throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Unmatching clients", "Unmatching clients"); + } - userSession.setLastSessionRefresh(currentTime); + if (oldToken.getIssuedAt() < client.getNotBefore()) { + throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token"); + } + if (oldToken.getIssuedAt() < realm.getNotBefore()) { + throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token"); + } - AccessTokenResponse res = responseBuilder(realm, client, event, session, userSession, clientSession) - .accessToken(accessToken) + + // recreate token. + Set requestedRoles = TokenManager.getAccess(null, clientSession.getClient(), user); + AccessToken newToken = createClientAccessToken(session, requestedRoles, realm, client, user, userSession, clientSession); + verifyAccess(oldToken, newToken); + + return new TokenValidation(user, userSession, clientSession, newToken); + + + } + + public AccessTokenResponse refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel authorizedClient, String encodedRefreshToken, EventBuilder event) throws OAuthErrorException { + RefreshToken refreshToken = verifyRefreshToken(realm, encodedRefreshToken); + + event.user(refreshToken.getSubject()).session(refreshToken.getSessionState()).detail(Details.REFRESH_TOKEN_ID, refreshToken.getId()); + + TokenValidation validation = validateToken(session, uriInfo, connection, realm, refreshToken); + // validate authorizedClient is same as validated client + if (!validation.clientSession.getClient().getId().equals(authorizedClient.getId())) { + throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token. Token client and authorized client don't match"); + } + + int currentTime = Time.currentTime(); + validation.userSession.setLastSessionRefresh(currentTime); + + AccessTokenResponse res = responseBuilder(realm, authorizedClient, event, session, validation.userSession, validation.clientSession) + .accessToken(validation.newToken) .generateIDToken() .generateRefreshToken().build(); return res; @@ -198,41 +226,27 @@ public class TokenManager { return requestedRoles; } - public void verifyAccess(AccessToken token, RealmModel realm, ClientModel client, UserModel user) throws OAuthErrorException { - ApplicationModel clientApp = (client instanceof ApplicationModel) ? (ApplicationModel)client : null; - - + public void verifyAccess(AccessToken token, AccessToken newToken) throws OAuthErrorException { if (token.getRealmAccess() != null) { + if (newToken.getRealmAccess() == null) throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for realm roles"); + for (String roleName : token.getRealmAccess().getRoles()) { - RoleModel role = realm.getRole(roleName); - if (role == null) { - throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid realm role " + roleName); - } - if (!user.hasRole(role)) { + if (!newToken.getRealmAccess().getRoles().contains(roleName)) { throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for realm role: " + roleName); } - if (!client.hasScope(role)) { - throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has realm scope: " + roleName); - } } } if (token.getResourceAccess() != null) { for (Map.Entry entry : token.getResourceAccess().entrySet()) { - ApplicationModel app = realm.getApplicationByName(entry.getKey()); - if (app == null) { - throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Application no longer exists", "Application no longer exists: " + entry.getKey()); + AccessToken.Access appAccess = newToken.getResourceAccess(entry.getKey()); + if (appAccess == null && !entry.getValue().getRoles().isEmpty()) { + throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User or application no longer has role permissions for application key: " + entry.getKey()); + } for (String roleName : entry.getValue().getRoles()) { - RoleModel role = app.getRole(roleName); - if (role == null) { - throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown application role: " + roleName); - } - if (!user.hasRole(role)) { + if (!appAccess.getRoles().contains(roleName)) { throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for application role " + roleName); } - if (clientApp != null && !clientApp.equals(app) && !client.hasScope(role)) { - throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has application scope" + roleName); - } } } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddressMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java similarity index 95% rename from services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddressMapper.java rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java index 235f2abfd7..d91f7eacbf 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddressMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java @@ -21,7 +21,7 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCAddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCFullNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java similarity index 72% rename from services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCFullNameMapper.java rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java index f91abe5515..000f520a8f 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCFullNameMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java @@ -5,11 +5,15 @@ 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.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.AccessToken; import org.keycloak.representations.IDToken; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Set the 'name' claim to be first + last name. @@ -17,7 +21,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCFullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); @@ -88,4 +92,21 @@ public class OIDCFullNameMapper extends AbstractOIDCProtocolMapper implements OI setClaim(token, userSession); return token; } + + public static ProtocolMapperModel create(String name, + boolean consentRequired, String consentText, + boolean accessToken, boolean idToken) { + ProtocolMapperModel mapper = new ProtocolMapperModel(); + mapper.setName(name); + mapper.setProtocolMapper(PROVIDER_ID); + mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + mapper.setConsentRequired(consentRequired); + mapper.setConsentText(consentText); + Map config = new HashMap(); + if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + mapper.setConfig(config); + return mapper; + } + } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddClaimMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java similarity index 76% rename from services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddClaimMapper.java rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java index fad7bd6795..b54c919c76 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddClaimMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java @@ -6,11 +6,14 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.ProtocolMapperUtils; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.AccessToken; import org.keycloak.representations.IDToken; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @@ -18,7 +21,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCAddClaimMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); @@ -62,7 +65,7 @@ public class OIDCAddClaimMapper extends AbstractOIDCProtocolMapper implements OI } - public static final String PROVIDER_ID = "oidc-add-claim-mapper"; + public static final String PROVIDER_ID = "oidc-hardcoded-claim-mapper"; public List getConfigProperties() { @@ -76,7 +79,7 @@ public class OIDCAddClaimMapper extends AbstractOIDCProtocolMapper implements OI @Override public String getDisplayType() { - return "Hard coded claim"; + return "Hardcoded claim"; } @Override @@ -111,16 +114,25 @@ public class OIDCAddClaimMapper extends AbstractOIDCProtocolMapper implements OI return token; } - public static ProtocolMapperModel createClaimMapper(String name, - String userAttribute, - String tokenClaimName, String claimType, + public static ProtocolMapperModel create(String name, + String hardcodedName, + String hardcodedValue, String claimType, boolean consentRequired, String consentText, boolean accessToken, boolean idToken) { - return OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute, - tokenClaimName, claimType, - consentRequired, consentText, - accessToken, idToken, - PROVIDER_ID); + ProtocolMapperModel mapper = new ProtocolMapperModel(); + mapper.setName(name); + mapper.setProtocolMapper(PROVIDER_ID); + mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + mapper.setConsentRequired(consentRequired); + mapper.setConsentText(consentText); + Map config = new HashMap(); + config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, hardcodedName); + config.put(CLAIM_VALUE, hardcodedValue); + config.put(OIDCAttributeMapperHelper.JSON_TYPE, claimType); + if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + mapper.setConfig(config); + return mapper; } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddRoleMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java similarity index 72% rename from services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddRoleMapper.java rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java index c46b9d48ee..ec0f720c03 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAddRoleMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java @@ -4,10 +4,13 @@ import org.keycloak.models.ClientSessionModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.AccessToken; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Add a role to a token @@ -15,7 +18,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCAddRoleMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { +public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { private static final List configProperties = new ArrayList(); @@ -31,7 +34,7 @@ public class OIDCAddRoleMapper extends AbstractOIDCProtocolMapper implements OID configProperties.add(property); } - public static final String PROVIDER_ID = "oidc-role-mapper"; + public static final String PROVIDER_ID = "oidc-hardcoded-role-mapper"; public List getConfigProperties() { @@ -78,4 +81,19 @@ public class OIDCAddRoleMapper extends AbstractOIDCProtocolMapper implements OID } return token; } + + public static ProtocolMapperModel create(String name, + String role) { + String mapperId = PROVIDER_ID; + ProtocolMapperModel mapper = new ProtocolMapperModel(); + mapper.setName(name); + mapper.setProtocolMapper(mapperId); + mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + Map config = new HashMap(); + config.put(ROLE_CONFIG, role); + mapper.setConfig(config); + return mapper; + + } + } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCRoleMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java similarity index 78% rename from services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCRoleMapper.java rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java index 3ed9b6197c..f60246caa1 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCRoleMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java @@ -6,11 +6,14 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.ProtocolMapperUtils; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.AccessToken; import org.keycloak.representations.IDToken; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Map an assigned role to a different position and name in the token @@ -18,7 +21,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCRoleMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { +public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { private static final List configProperties = new ArrayList(); @@ -41,7 +44,7 @@ public class OIDCRoleMapper extends AbstractOIDCProtocolMapper implements OIDCAc configProperties.add(property); } - public static final String PROVIDER_ID = "oidc-role-mapper"; + public static final String PROVIDER_ID = "oidc-role-name-mapper"; public List getConfigProperties() { @@ -98,4 +101,21 @@ public class OIDCRoleMapper extends AbstractOIDCProtocolMapper implements OIDCAc } return token; } + + public static ProtocolMapperModel create(String name, + String role, + String newName) { + String mapperId = PROVIDER_ID; + ProtocolMapperModel mapper = new ProtocolMapperModel(); + mapper.setName(name); + mapper.setProtocolMapper(mapperId); + mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + Map config = new HashMap(); + config.put(ROLE_CONFIG, role); + config.put(NEW_ROLE_NAME, newName); + mapper.setConfig(config); + return mapper; + + } + } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java similarity index 95% rename from services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java index b2ea92dc9e..906eef4f15 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java @@ -21,7 +21,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java similarity index 95% rename from services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java index 3b559e26ac..a47f824789 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java @@ -21,7 +21,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); static { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserSessionNoteMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java old mode 100644 new mode 100755 similarity index 97% rename from services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserSessionNoteMapper.java rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java index effa69af83..4e83120d24 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserSessionNoteMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java @@ -19,7 +19,7 @@ import org.keycloak.representations.IDToken; * * @author Marek Posolda */ -public class OIDCUserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { private static final List configProperties = new ArrayList(); 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 f8cc934068..4ea322a53c 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 @@ -1,10 +1,10 @@ -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 +org.keycloak.protocol.oidc.mappers.UserAttributeMapper +org.keycloak.protocol.oidc.mappers.FullNameMapper +org.keycloak.protocol.oidc.mappers.UserPropertyMapper +org.keycloak.protocol.oidc.mappers.AddressMapper +org.keycloak.protocol.oidc.mappers.HardcodedClaim +org.keycloak.protocol.oidc.mappers.HardcodedRole +org.keycloak.protocol.oidc.mappers.RoleNameMapper +org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java index 89ed153d44..6497cb50f1 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -276,8 +276,8 @@ public class AccountTest { events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT).assertEvent(); - Assert.assertEquals("", profilePage.getFirstName()); - Assert.assertEquals("", profilePage.getLastName()); + Assert.assertEquals("Tom", profilePage.getFirstName()); + Assert.assertEquals("Brady", profilePage.getLastName()); Assert.assertEquals("test-user@localhost", profilePage.getEmail()); // All fields are required, so there should be an error when something is missing. @@ -310,8 +310,8 @@ public class AccountTest { profilePage.clickCancel(); - Assert.assertEquals("", profilePage.getFirstName()); - Assert.assertEquals("", profilePage.getLastName()); + Assert.assertEquals("Tom", profilePage.getFirstName()); + Assert.assertEquals("Brady", profilePage.getLastName()); Assert.assertEquals("test-user@localhost", profilePage.getEmail()); events.assertEmpty(); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java old mode 100644 new mode 100755 index 2cbe7aeed2..9d59ad9899 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java @@ -17,7 +17,6 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.keycloak.OAuth2Constants; import org.keycloak.adapters.HttpClientBuilder; import org.keycloak.events.Details; import org.keycloak.federation.kerberos.CommonKerberosConfig; @@ -31,15 +30,11 @@ import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; import org.keycloak.protocol.oidc.OIDCLoginProtocol; -import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; -import org.keycloak.protocol.oidc.mappers.OIDCUserSessionNoteMapper; +import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.OAuthClient; -import org.keycloak.testsuite.adapter.AdapterTest; -import org.keycloak.testsuite.adapter.AdapterTestStrategy; import org.keycloak.testsuite.pages.AccountPasswordPage; -import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.WebResource; @@ -182,7 +177,7 @@ public abstract class AbstractKerberosTest { @Override public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { - ProtocolMapperModel protocolMapper = OIDCUserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, + ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, KerberosConstants.GSS_DELEGATION_CREDENTIAL, KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String", true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java index 6fca683cb3..0091e337c6 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java @@ -40,8 +40,12 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; -import org.keycloak.protocol.oidc.mappers.OIDCAddressMapper; -import org.keycloak.protocol.oidc.mappers.OIDCUserAttributeMapper; +import org.keycloak.protocol.oidc.mappers.AddressMapper; +import org.keycloak.protocol.oidc.mappers.FullNameMapper; +import org.keycloak.protocol.oidc.mappers.HardcodedClaim; +import org.keycloak.protocol.oidc.mappers.HardcodedRole; +import org.keycloak.protocol.oidc.mappers.RoleNameMapper; +import org.keycloak.protocol.oidc.mappers.UserAttributeMapper; import org.keycloak.representations.AccessToken; import org.keycloak.representations.IDToken; import org.keycloak.services.managers.RealmManager; @@ -67,6 +71,7 @@ import javax.ws.rs.core.UriBuilder; import java.io.IOException; import java.net.URI; import java.util.HashMap; +import java.util.Map; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; @@ -594,9 +599,14 @@ public class AccessTokenTest { user.setAttribute("country", "USA"); user.setAttribute("phone", "617-777-6666"); ApplicationModel app = realm.getApplicationByName("test-app"); - ProtocolMapperModel mapper = OIDCAddressMapper.createAddressMapper(true, true); + ProtocolMapperModel mapper = AddressMapper.createAddressMapper(true, true); app.addProtocolMapper(mapper); - app.addProtocolMapper(OIDCUserAttributeMapper.createClaimMapper("custom phone", "phone", "home_phone", "String", true, "", true, true)); + app.addProtocolMapper(HardcodedClaim.create("hard", "hard", "coded", "String", false, null, true, true)); + app.addProtocolMapper(HardcodedClaim.create("hard-nested", "nested.hard", "coded-nested", "String", false, null, true, true)); + app.addProtocolMapper(UserAttributeMapper.createClaimMapper("custom phone", "phone", "home_phone", "String", true, "", true, true)); + app.addProtocolMapper(UserAttributeMapper.createClaimMapper("nested phone", "phone", "home.phone", "String", true, "", true, true)); + app.addProtocolMapper(HardcodedRole.create("hard-realm", "hardcoded")); + app.addProtocolMapper(HardcodedRole.create("hard-app", "app.hardcoded")); session.getTransaction().commit(); session.close(); } @@ -607,15 +617,22 @@ public class AccessTokenTest { org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); IDToken idToken = getIdToken(tokenResponse); Assert.assertNotNull(idToken.getAddress()); + Assert.assertEquals(idToken.getName(), "Tom Brady"); Assert.assertEquals(idToken.getAddress().getStreetAddress(), "5 Yawkey Way"); Assert.assertEquals(idToken.getAddress().getLocality(), "Boston"); Assert.assertEquals(idToken.getAddress().getRegion(), "MA"); Assert.assertEquals(idToken.getAddress().getPostalCode(), "02115"); Assert.assertEquals(idToken.getAddress().getCountry(), "USA"); Assert.assertNotNull(idToken.getOtherClaims().get("home_phone")); - //Assert.assertEquals("617-777-6666", idToken.getOtherClaims().get("home_phone")); + Assert.assertEquals("617-777-6666", idToken.getOtherClaims().get("home_phone")); + Assert.assertEquals("coded", idToken.getOtherClaims().get("hard")); + Map nested = (Map)idToken.getOtherClaims().get("nested"); + Assert.assertEquals("coded-nested", nested.get("hard")); + nested = (Map)idToken.getOtherClaims().get("home"); + Assert.assertEquals("617-777-6666", nested.get("phone")); AccessToken accessToken = getAccessToken(tokenResponse); + Assert.assertEquals(accessToken.getName(), "Tom Brady"); Assert.assertNotNull(accessToken.getAddress()); Assert.assertEquals(accessToken.getAddress().getStreetAddress(), "5 Yawkey Way"); Assert.assertEquals(accessToken.getAddress().getLocality(), "Boston"); @@ -624,11 +641,41 @@ public class AccessTokenTest { Assert.assertEquals(accessToken.getAddress().getCountry(), "USA"); Assert.assertNotNull(accessToken.getOtherClaims().get("home_phone")); Assert.assertEquals("617-777-6666", accessToken.getOtherClaims().get("home_phone")); + Assert.assertEquals("coded", accessToken.getOtherClaims().get("hard")); + nested = (Map)accessToken.getOtherClaims().get("nested"); + Assert.assertEquals("coded-nested", nested.get("hard")); + nested = (Map)accessToken.getOtherClaims().get("home"); + Assert.assertEquals("617-777-6666", nested.get("phone")); + Assert.assertTrue(accessToken.getRealmAccess().getRoles().contains("hardcoded")); + Assert.assertTrue(accessToken.getResourceAccess("app").getRoles().contains("hardcoded")); response.close(); } client.close(); + + // undo mappers + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + ApplicationModel app = realm.getApplicationByName("test-app"); + for (ProtocolMapperModel model : app.getProtocolMappers()) { + if (model.getName().equals("address") + || model.getName().equals("hard") + || model.getName().equals("hard-nested") + || model.getName().equals("custom phone") + || model.getName().equals("nested phone") + || model.getName().equals("hard-realm") + || model.getName().equals("hard-app") + ) { + app.removeProtocolMapper(model); + } + } + session.getTransaction().commit(); + session.close(); + } + + events.clear(); } @@ -645,7 +692,7 @@ public class AccessTokenTest { } private AccessToken getAccessToken(org.keycloak.representations.AccessTokenResponse tokenResponse) throws VerificationException { - JWSInput input = new JWSInput(tokenResponse.getIdToken()); + JWSInput input = new JWSInput(tokenResponse.getToken()); AccessToken idToken = null; try { idToken = input.readJsonContent(AccessToken.class); 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 8f8f2b492f..b8e9e1c861 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 @@ -3,21 +3,27 @@ package org.keycloak.testsuite.saml; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput; import org.junit.Assert; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.keycloak.Config; import org.keycloak.models.ApplicationModel; import org.keycloak.models.Constants; 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.oidc.TokenManager; +import org.keycloak.protocol.saml.mappers.AttributeStatementHelper; +import org.keycloak.protocol.saml.mappers.HardcodedAttributeMapper; +import org.keycloak.protocol.saml.mappers.HardcodedRole; +import org.keycloak.protocol.saml.mappers.RoleListMapper; +import org.keycloak.protocol.saml.mappers.RoleNameMapper; import org.keycloak.representations.AccessToken; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.admin.AdminRoot; import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; @@ -101,10 +107,12 @@ public class SamlBindingTest { private void handler(HttpServletRequest req, HttpServletResponse resp) { System.out.println("********* HERE ******"); if (req.getParameterMap().isEmpty()) { + System.out.println("redirecting"); 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; } + System.out.println("received response"); samlResponse = req.getParameter("SAMLResponse"); } } @@ -199,43 +207,122 @@ public class SamlBindingTest { // 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 + { + 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(); - 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; + // test attributes and roles + + boolean email = false; + boolean phone = false; + boolean userRole = false; + boolean managerRole = 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; + } else if (attr.getName().equals("Role")) { + if (attr.getAttributeValue().get(0).equals("manager")) managerRole = true; + if (attr.getAttributeValue().get(0).equals("user")) userRole = true; + } } + } + Assert.assertTrue(email); + Assert.assertTrue(phone); + Assert.assertTrue(userRole); + Assert.assertTrue(managerRole); } - Assert.assertTrue(email); - Assert.assertTrue(phone); + keycloakRule.update(new KeycloakRule.KeycloakSetup() { + @Override + public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { + ApplicationModel app = appRealm.getApplicationByName("http://localhost:8081/employee/"); + for (ProtocolMapperModel mapper : app.getProtocolMappers()) { + if (mapper.getName().equals("role-list")) { + app.removeProtocolMapper(mapper); + mapper.setId(null); + mapper.getConfig().put(RoleListMapper.SINGLE_ROLE_ATTRIBUTE, "true"); + mapper.getConfig().put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "memberOf"); + app.addProtocolMapper(mapper); + } + } + app.addProtocolMapper(HardcodedAttributeMapper.create("hardcoded-attribute", "hardcoded-attribute", "Basic", null, "hard", false, null)); + app.addProtocolMapper(HardcodedRole.create("hardcoded-role", "hardcoded-role")); + app.addProtocolMapper(RoleNameMapper.create("renamed-role", "manager", "el-jefe")); + app.addProtocolMapper(RoleNameMapper.create("renamed-employee-role", "http://localhost:8081/employee/.employee", "pee-on")); + } + }, "demo"); + System.out.println(">>>>>>>>>> single role attribute <<<<<<<<"); + + { + SamlSPFacade.samlResponse = null; + driver.navigate().to("http://localhost:8081/employee/"); + System.out.println(driver.getCurrentUrl()); + 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 and roles + + boolean userRole = false; + boolean managerRole = false; + boolean single = false; + boolean hardcodedRole = false; + boolean hardcodedAttribute = false; + boolean peeOn = false; + for (AttributeStatementType statement : assertion.getAttributeStatements()) { + for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) { + AttributeType attr = choice.getAttribute(); + if (attr.getName().equals("memberOf")) { + if (single) Assert.fail("too many role attributes"); + single = true; + for (Object value : attr.getAttributeValue()) { + if (value.equals("el-jefe")) managerRole = true; + if (value.equals("user")) userRole = true; + if (value.equals("hardcoded-role")) hardcodedRole = true; + if (value.equals("pee-on")) peeOn = true; + } + } else if (attr.getName().equals("hardcoded-attribute")) { + hardcodedAttribute = true; + Assert.assertEquals(attr.getAttributeValue().get(0), "hard"); + } + } + + } + + Assert.assertTrue(single); + Assert.assertTrue(hardcodedAttribute); + Assert.assertTrue(hardcodedRole); + Assert.assertTrue(peeOn); + Assert.assertTrue(userRole); + Assert.assertTrue(managerRole); + } } @Test diff --git a/testsuite/integration/src/test/resources/saml/testsaml.json b/testsuite/integration/src/test/resources/saml/testsaml.json index 3cd3342b6b..cac873b807 100755 --- a/testsuite/integration/src/test/resources/saml/testsaml.json +++ b/testsuite/integration/src/test/resources/saml/testsaml.json @@ -27,7 +27,10 @@ "attributes" : { "phone": "617" }, - "realmRoles": ["manager", "user"] + "realmRoles": ["manager", "user"], + "applicationRoles": { + "http://localhost:8081/employee/": [ "employee" ] + } } ], "applications": [ @@ -304,6 +307,14 @@ "name": "user", "description": "Have User privileges" } - ] + ], + "application" : { + "http://localhost:8081/employee/" : [ + { + "name": "employee", + "description": "Have Employee privileges" + } + ] + } } } diff --git a/testsuite/integration/src/test/resources/testrealm.json b/testsuite/integration/src/test/resources/testrealm.json index 81a442d7a8..17c54dcc7b 100755 --- a/testsuite/integration/src/test/resources/testrealm.json +++ b/testsuite/integration/src/test/resources/testrealm.json @@ -20,6 +20,8 @@ "username" : "test-user@localhost", "enabled": true, "email" : "test-user@localhost", + "firstName": "Tom", + "lastName": "Brady", "credentials" : [ { "type" : "password", "value" : "password" }