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