From 89e3d757b9f6893d16e5c3e49fc407aa1a003931 Mon Sep 17 00:00:00 2001 From: Matthias Piepkorn <mpiepk@gmail.com> Date: Fri, 16 Nov 2018 19:55:52 +0000 Subject: [PATCH] update for KEYCLOAK-6884 KEYCLOAK-3454 KEYCLOAK-8298 --- src/main/java/org/keycloak/protocol/cas/mappers/GroupMembershipMapper.java | 7 +- src/main/java/org/keycloak/protocol/cas/mappers/HardcodedClaim.java | 5 + src/main/java/org/keycloak/protocol/cas/mappers/UserRealmRoleMappingMapper.java | 15 ++++ src/main/java/org/keycloak/protocol/cas/mappers/UserAttributeMapper.java | 7 +- src/main/java/org/keycloak/protocol/cas/mappers/UserClientRoleMappingMapper.java | 50 ++++++---------- src/main/java/org/keycloak/protocol/cas/mappers/CASAttributeMapper.java | 5 + src/main/java/org/keycloak/protocol/cas/endpoints/ServiceValidateEndpoint.java | 2 src/main/java/org/keycloak/protocol/cas/mappers/AbstractUserRoleMappingMapper.java | 65 +++------------------ src/main/java/org/keycloak/protocol/cas/mappers/FullNameMapper.java | 7 +- src/main/java/org/keycloak/protocol/cas/mappers/UserPropertyMapper.java | 7 +- src/main/java/org/keycloak/protocol/cas/mappers/UserSessionNoteMapper.java | 5 + 11 files changed, 66 insertions(+), 109 deletions(-) diff --git a/src/main/java/org/keycloak/protocol/cas/endpoints/ServiceValidateEndpoint.java b/src/main/java/org/keycloak/protocol/cas/endpoints/ServiceValidateEndpoint.java index f060435..291b74e 100644 --- a/src/main/java/org/keycloak/protocol/cas/endpoints/ServiceValidateEndpoint.java +++ b/src/main/java/org/keycloak/protocol/cas/endpoints/ServiceValidateEndpoint.java @@ -36,7 +36,7 @@ for (ProtocolMapperModel mapping : mappings) { ProtocolMapper mapper = (ProtocolMapper) sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper()); if (mapper instanceof CASAttributeMapper) { - ((CASAttributeMapper) mapper).setAttribute(attributes, mapping, userSession); + ((CASAttributeMapper) mapper).setAttribute(attributes, mapping, userSession, session, clientSessionCtx); } } diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/AbstractUserRoleMappingMapper.java b/src/main/java/org/keycloak/protocol/cas/mappers/AbstractUserRoleMappingMapper.java index 39ae848..4408d7d 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/AbstractUserRoleMappingMapper.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/AbstractUserRoleMappingMapper.java @@ -17,14 +17,11 @@ package org.keycloak.protocol.cas.mappers; -import org.keycloak.models.*; -import org.keycloak.models.utils.RoleUtils; +import org.keycloak.models.ProtocolMapperModel; import java.util.Map; import java.util.Set; -import java.util.function.Predicate; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Base class for mapping of user role mappings to an ID and Access Token claim. @@ -34,64 +31,22 @@ abstract class AbstractUserRoleMappingMapper extends AbstractCASProtocolMapper { /** - * Returns a stream with roles that come from: - * <ul> - * <li>Direct assignment of the role to the user</li> - * <li>Direct assignment of the role to any group of the user or any of its parent group</li> - * <li>Composite roles are expanded recursively, the composite role itself is also contained in the returned stream</li> - * </ul> - * @param user User to enumerate the roles for - */ - public Stream<RoleModel> getAllUserRolesStream(UserModel user) { - return Stream.concat( - user.getRoleMappings().stream(), - user.getGroups().stream() - .flatMap(this::groupAndItsParentsStream) - .flatMap(g -> g.getRoleMappings().stream())) - .flatMap(RoleUtils::expandCompositeRolesStream); - } - - /** - * Returns stream of the given group and its parents (recursively). - * @param group - * @return - */ - private Stream<GroupModel> groupAndItsParentsStream(GroupModel group) { - Stream.Builder<GroupModel> sb = Stream.builder(); - while (group != null) { - sb.add(group); - group = group.getParent(); - } - return sb.build(); - } - - /** * Retrieves all roles of the current user based on direct roles set to the user, its groups and their parent groups. * Then it recursively expands all composite roles, and restricts according to the given predicate {@code restriction}. * If the current client sessions is restricted (i.e. no client found in active user session has full scope allowed), * the final list of roles is also restricted by the client scope. Finally, the list is mapped to the token into * a claim. */ - protected void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, - Predicate<RoleModel> restriction, String prefix) { - String rolePrefix = prefix == null ? "" : prefix; - UserModel user = userSession.getUser(); - - // get a set of all realm roles assigned to the user or its group - Stream<RoleModel> clientUserRoles = getAllUserRolesStream(user).filter(restriction); - - boolean dontLimitScope = userSession.getAuthenticatedClientSessions().values().stream().anyMatch(cs -> cs.getClient().isFullScopeAllowed()); - if (! dontLimitScope) { - Set<RoleModel> clientRoles = userSession.getAuthenticatedClientSessions().values().stream() - .flatMap(cs -> cs.getClient().getScopeMappings().stream()) - .collect(Collectors.toSet()); - - clientUserRoles = clientUserRoles.filter(clientRoles::contains); + protected void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, Set<String> rolesToAdd, + String prefix) { + Set<String> realmRoleNames; + if (prefix != null && !prefix.isEmpty()) { + realmRoleNames = rolesToAdd.stream() + .map(roleName -> prefix + roleName) + .collect(Collectors.toSet()); + } else { + realmRoleNames = rolesToAdd; } - - Set<String> realmRoleNames = clientUserRoles - .map(m -> rolePrefix + m.getName()) - .collect(Collectors.toSet()); setPlainAttribute(attributes, mappingModel, realmRoleNames); } diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/CASAttributeMapper.java b/src/main/java/org/keycloak/protocol/cas/mappers/CASAttributeMapper.java index fccc99b..470bc5b 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/CASAttributeMapper.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/CASAttributeMapper.java @@ -1,10 +1,13 @@ package org.keycloak.protocol.cas.mappers; +import org.keycloak.models.ClientSessionContext; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserSessionModel; import java.util.Map; public interface CASAttributeMapper { - void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession); + void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, + KeycloakSession session, ClientSessionContext clientSessionCtx); } diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/FullNameMapper.java b/src/main/java/org/keycloak/protocol/cas/mappers/FullNameMapper.java index 3d889be..baf8257 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/FullNameMapper.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/FullNameMapper.java @@ -1,8 +1,6 @@ package org.keycloak.protocol.cas.mappers; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserSessionModel; +import org.keycloak.models.*; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.provider.ProviderConfigProperty; @@ -41,7 +39,8 @@ } @Override - public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, + KeycloakSession session, ClientSessionContext clientSessionCt) { UserModel user = userSession.getUser(); String first = user.getFirstName() == null ? "" : user.getFirstName() + " "; String last = user.getLastName() == null ? "" : user.getLastName(); diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/GroupMembershipMapper.java b/src/main/java/org/keycloak/protocol/cas/mappers/GroupMembershipMapper.java index bee3b9e..e5a9a89 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/GroupMembershipMapper.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/GroupMembershipMapper.java @@ -1,8 +1,6 @@ package org.keycloak.protocol.cas.mappers; -import org.keycloak.models.GroupModel; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.UserSessionModel; +import org.keycloak.models.*; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.provider.ProviderConfigProperty; @@ -52,7 +50,8 @@ } @Override - public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, + KeycloakSession session, ClientSessionContext clientSessionCt) { List<String> membership = new LinkedList<>(); boolean fullPath = useFullPath(mappingModel); for (GroupModel group : userSession.getUser().getGroups()) { diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/HardcodedClaim.java b/src/main/java/org/keycloak/protocol/cas/mappers/HardcodedClaim.java index 42c7535..f66c469 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/HardcodedClaim.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/HardcodedClaim.java @@ -1,5 +1,7 @@ package org.keycloak.protocol.cas.mappers; +import org.keycloak.models.ClientSessionContext; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; @@ -53,7 +55,8 @@ } @Override - public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, + KeycloakSession session, ClientSessionContext clientSessionCt) { setMappedAttribute(attributes, mappingModel, mappingModel.getConfig().get(CLAIM_VALUE)); } diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/UserAttributeMapper.java b/src/main/java/org/keycloak/protocol/cas/mappers/UserAttributeMapper.java index cbca99c..1ec125d 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/UserAttributeMapper.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/UserAttributeMapper.java @@ -1,8 +1,6 @@ package org.keycloak.protocol.cas.mappers; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserSessionModel; +import org.keycloak.models.*; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; @@ -66,7 +64,8 @@ } @Override - public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, + KeycloakSession session, ClientSessionContext clientSessionCt) { UserModel user = userSession.getUser(); String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); boolean aggregateAttrs = Boolean.valueOf(mappingModel.getConfig().get(ProtocolMapperUtils.AGGREGATE_ATTRS)); diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/UserClientRoleMappingMapper.java b/src/main/java/org/keycloak/protocol/cas/mappers/UserClientRoleMappingMapper.java index ff872d3..1b236e4 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/UserClientRoleMappingMapper.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/UserClientRoleMappingMapper.java @@ -2,12 +2,13 @@ import org.keycloak.models.*; import org.keycloak.protocol.ProtocolMapperUtils; -import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.representations.AccessToken; +import org.keycloak.utils.RoleResolveUtil; import java.util.*; -import java.util.function.Predicate; +import java.util.stream.Collectors; public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper { @@ -60,40 +61,25 @@ } @Override - public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, + KeycloakSession session, ClientSessionContext clientSessionCtx) { String clientId = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID); String rolePrefix = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_ROLE_PREFIX); - setAttribute(attributes, mappingModel, userSession, getClientRoleFilter(clientId, userSession), rolePrefix); - } - - private static Predicate<RoleModel> getClientRoleFilter(String clientId, UserSessionModel userSession) { - if (clientId == null) { - return RoleModel::isClientRole; + if (clientId != null && !clientId.isEmpty()) { + AccessToken.Access access = RoleResolveUtil.getResolvedClientRoles(session, clientSessionCtx, clientId, false); + if (access == null) { + return; + } + setAttribute(attributes, mappingModel, access.getRoles(), rolePrefix); + } else { + // If clientId is not specified, we consider all clients + Map<String, AccessToken.Access> allAccess = RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx); + Set<String> allRoles = allAccess.values().stream().filter(Objects::nonNull) + .flatMap(access -> access.getRoles().stream()) + .collect(Collectors.toSet()); + setAttribute(attributes, mappingModel, allRoles, rolePrefix); } - - RealmModel clientRealm = userSession.getRealm(); - ClientModel client = clientRealm.getClientByClientId(clientId.trim()); - - if (client == null) { - return RoleModel::isClientRole; - } - - boolean fullScopeAllowed = client.isFullScopeAllowed(); - Set<RoleModel> clientRoleMappings = client.getRoles(); - if (fullScopeAllowed) { - return clientRoleMappings::contains; - } - - Set<RoleModel> scopeMappings = new HashSet<>(); - - // CAS protocol does not support scopes, so pass null scopeParam - Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(null, client); - for (ClientScopeModel clientScope : clientScopes) { - scopeMappings.addAll(clientScope.getScopeMappings()); - } - - return role -> clientRoleMappings.contains(role) && scopeMappings.contains(role); } public static ProtocolMapperModel create(String clientId, String clientRolePrefix, diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/UserPropertyMapper.java b/src/main/java/org/keycloak/protocol/cas/mappers/UserPropertyMapper.java index 66f09be..24c9af8 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/UserPropertyMapper.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/UserPropertyMapper.java @@ -1,8 +1,6 @@ package org.keycloak.protocol.cas.mappers; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserSessionModel; +import org.keycloak.models.*; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.provider.ProviderConfigProperty; @@ -50,7 +48,8 @@ } @Override - public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, + KeycloakSession session, ClientSessionContext clientSessionCt) { UserModel user = userSession.getUser(); String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName); diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/UserRealmRoleMappingMapper.java b/src/main/java/org/keycloak/protocol/cas/mappers/UserRealmRoleMappingMapper.java index 41a6a78..1c3094f 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/UserRealmRoleMappingMapper.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/UserRealmRoleMappingMapper.java @@ -1,10 +1,14 @@ package org.keycloak.protocol.cas.mappers; +import org.keycloak.models.ClientSessionContext; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.representations.AccessToken; +import org.keycloak.utils.RoleResolveUtil; import java.util.ArrayList; import java.util.List; @@ -53,9 +57,16 @@ } @Override - public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, + KeycloakSession session, ClientSessionContext clientSessionCtx) { String rolePrefix = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX); - setAttribute(attributes, mappingModel, userSession, role -> ! role.isClientRole(), rolePrefix); + + AccessToken.Access access = RoleResolveUtil.getResolvedRealmRoles(session, clientSessionCtx, false); + if (access == null) { + return; + } + + setAttribute(attributes, mappingModel, access.getRoles(), rolePrefix); } public static ProtocolMapperModel create(String realmRolePrefix, String name, String tokenClaimName) { diff --git a/src/main/java/org/keycloak/protocol/cas/mappers/UserSessionNoteMapper.java b/src/main/java/org/keycloak/protocol/cas/mappers/UserSessionNoteMapper.java index f718aab..f79c1f7 100644 --- a/src/main/java/org/keycloak/protocol/cas/mappers/UserSessionNoteMapper.java +++ b/src/main/java/org/keycloak/protocol/cas/mappers/UserSessionNoteMapper.java @@ -1,5 +1,7 @@ package org.keycloak.protocol.cas.mappers; +import org.keycloak.models.ClientSessionContext; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.ProtocolMapperUtils; @@ -54,7 +56,8 @@ } @Override - public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, + KeycloakSession session, ClientSessionContext clientSessionCt) { String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE); String noteValue = userSession.getNote(noteName); if (noteValue == null) return; -- Gitblit v1.9.1