Commit 89e3d757 authored by Matthias Piepkorn's avatar Matthias Piepkorn Committed by Doccrazy

update for KEYCLOAK-6884 KEYCLOAK-3454 KEYCLOAK-8298

parent f63e4079
...@@ -36,7 +36,7 @@ public class ServiceValidateEndpoint extends ValidateEndpoint { ...@@ -36,7 +36,7 @@ public class ServiceValidateEndpoint extends ValidateEndpoint {
for (ProtocolMapperModel mapping : mappings) { for (ProtocolMapperModel mapping : mappings) {
ProtocolMapper mapper = (ProtocolMapper) sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper()); ProtocolMapper mapper = (ProtocolMapper) sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
if (mapper instanceof CASAttributeMapper) { if (mapper instanceof CASAttributeMapper) {
((CASAttributeMapper) mapper).setAttribute(attributes, mapping, userSession); ((CASAttributeMapper) mapper).setAttribute(attributes, mapping, userSession, session, clientSessionCtx);
} }
} }
......
...@@ -17,14 +17,11 @@ ...@@ -17,14 +17,11 @@
package org.keycloak.protocol.cas.mappers; package org.keycloak.protocol.cas.mappers;
import org.keycloak.models.*; import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.utils.RoleUtils;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors; 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. * Base class for mapping of user role mappings to an ID and Access Token claim.
...@@ -33,38 +30,6 @@ import java.util.stream.Stream; ...@@ -33,38 +30,6 @@ import java.util.stream.Stream;
*/ */
abstract class AbstractUserRoleMappingMapper extends AbstractCASProtocolMapper { 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. * 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}. * Then it recursively expands all composite roles, and restricts according to the given predicate {@code restriction}.
...@@ -72,27 +37,17 @@ abstract class AbstractUserRoleMappingMapper extends AbstractCASProtocolMapper { ...@@ -72,27 +37,17 @@ abstract class AbstractUserRoleMappingMapper extends AbstractCASProtocolMapper {
* the final list of roles is also restricted by the client scope. Finally, the list is mapped to the token into * the final list of roles is also restricted by the client scope. Finally, the list is mapped to the token into
* a claim. * a claim.
*/ */
protected void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession, protected void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, Set<String> rolesToAdd,
Predicate<RoleModel> restriction, String prefix) { String prefix) {
String rolePrefix = prefix == null ? "" : prefix; Set<String> realmRoleNames;
UserModel user = userSession.getUser(); if (prefix != null && !prefix.isEmpty()) {
realmRoleNames = rolesToAdd.stream()
// get a set of all realm roles assigned to the user or its group .map(roleName -> prefix + roleName)
Stream<RoleModel> clientUserRoles = getAllUserRolesStream(user).filter(restriction); .collect(Collectors.toSet());
} else {
boolean dontLimitScope = userSession.getAuthenticatedClientSessions().values().stream().anyMatch(cs -> cs.getClient().isFullScopeAllowed()); realmRoleNames = rolesToAdd;
if (! dontLimitScope) {
Set<RoleModel> clientRoles = userSession.getAuthenticatedClientSessions().values().stream()
.flatMap(cs -> cs.getClient().getScopeMappings().stream())
.collect(Collectors.toSet());
clientUserRoles = clientUserRoles.filter(clientRoles::contains);
} }
Set<String> realmRoleNames = clientUserRoles
.map(m -> rolePrefix + m.getName())
.collect(Collectors.toSet());
setPlainAttribute(attributes, mappingModel, realmRoleNames); setPlainAttribute(attributes, mappingModel, realmRoleNames);
} }
} }
package org.keycloak.protocol.cas.mappers; 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.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import java.util.Map; import java.util.Map;
public interface CASAttributeMapper { 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);
} }
package org.keycloak.protocol.cas.mappers; package org.keycloak.protocol.cas.mappers;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.*;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
...@@ -41,7 +39,8 @@ public class FullNameMapper extends AbstractCASProtocolMapper { ...@@ -41,7 +39,8 @@ public class FullNameMapper extends AbstractCASProtocolMapper {
} }
@Override @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(); UserModel user = userSession.getUser();
String first = user.getFirstName() == null ? "" : user.getFirstName() + " "; String first = user.getFirstName() == null ? "" : user.getFirstName() + " ";
String last = user.getLastName() == null ? "" : user.getLastName(); String last = user.getLastName() == null ? "" : user.getLastName();
......
package org.keycloak.protocol.cas.mappers; package org.keycloak.protocol.cas.mappers;
import org.keycloak.models.GroupModel; import org.keycloak.models.*;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
...@@ -52,7 +50,8 @@ public class GroupMembershipMapper extends AbstractCASProtocolMapper { ...@@ -52,7 +50,8 @@ public class GroupMembershipMapper extends AbstractCASProtocolMapper {
} }
@Override @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<>(); List<String> membership = new LinkedList<>();
boolean fullPath = useFullPath(mappingModel); boolean fullPath = useFullPath(mappingModel);
for (GroupModel group : userSession.getUser().getGroups()) { for (GroupModel group : userSession.getUser().getGroups()) {
......
package org.keycloak.protocol.cas.mappers; 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.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
...@@ -53,7 +55,8 @@ public class HardcodedClaim extends AbstractCASProtocolMapper { ...@@ -53,7 +55,8 @@ public class HardcodedClaim extends AbstractCASProtocolMapper {
} }
@Override @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)); setMappedAttribute(attributes, mappingModel, mappingModel.getConfig().get(CLAIM_VALUE));
} }
......
package org.keycloak.protocol.cas.mappers; package org.keycloak.protocol.cas.mappers;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.*;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
...@@ -66,7 +64,8 @@ public class UserAttributeMapper extends AbstractCASProtocolMapper { ...@@ -66,7 +64,8 @@ public class UserAttributeMapper extends AbstractCASProtocolMapper {
} }
@Override @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(); UserModel user = userSession.getUser();
String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE);
boolean aggregateAttrs = Boolean.valueOf(mappingModel.getConfig().get(ProtocolMapperUtils.AGGREGATE_ATTRS)); boolean aggregateAttrs = Boolean.valueOf(mappingModel.getConfig().get(ProtocolMapperUtils.AGGREGATE_ATTRS));
......
...@@ -2,12 +2,13 @@ package org.keycloak.protocol.cas.mappers; ...@@ -2,12 +2,13 @@ package org.keycloak.protocol.cas.mappers;
import org.keycloak.models.*; import org.keycloak.models.*;
import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.utils.RoleResolveUtil;
import java.util.*; import java.util.*;
import java.util.function.Predicate; import java.util.stream.Collectors;
public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper { public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
...@@ -60,40 +61,25 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper { ...@@ -60,40 +61,25 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
} }
@Override @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 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); String rolePrefix = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_ROLE_PREFIX);
setAttribute(attributes, mappingModel, userSession, getClientRoleFilter(clientId, userSession), rolePrefix); if (clientId != null && !clientId.isEmpty()) {
} AccessToken.Access access = RoleResolveUtil.getResolvedClientRoles(session, clientSessionCtx, clientId, false);
if (access == null) {
private static Predicate<RoleModel> getClientRoleFilter(String clientId, UserSessionModel userSession) { return;
if (clientId == null) { }
return RoleModel::isClientRole; setAttribute(attributes, mappingModel, access.getRoles(), rolePrefix);
} } else {
// If clientId is not specified, we consider all clients
RealmModel clientRealm = userSession.getRealm(); Map<String, AccessToken.Access> allAccess = RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx);
ClientModel client = clientRealm.getClientByClientId(clientId.trim()); Set<String> allRoles = allAccess.values().stream().filter(Objects::nonNull)
.flatMap(access -> access.getRoles().stream())
if (client == null) { .collect(Collectors.toSet());
return RoleModel::isClientRole; setAttribute(attributes, mappingModel, allRoles, rolePrefix);
} }
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, public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
......
package org.keycloak.protocol.cas.mappers; package org.keycloak.protocol.cas.mappers;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.*;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
...@@ -50,7 +48,8 @@ public class UserPropertyMapper extends AbstractCASProtocolMapper { ...@@ -50,7 +48,8 @@ public class UserPropertyMapper extends AbstractCASProtocolMapper {
} }
@Override @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(); UserModel user = userSession.getUser();
String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE);
String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName); String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName);
......
package org.keycloak.protocol.cas.mappers; 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.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.utils.RoleResolveUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -53,9 +57,16 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper { ...@@ -53,9 +57,16 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
} }
@Override @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); 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) { public static ProtocolMapperModel create(String realmRolePrefix, String name, String tokenClaimName) {
......
package org.keycloak.protocol.cas.mappers; 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.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.ProtocolMapperUtils;
...@@ -54,7 +56,8 @@ public class UserSessionNoteMapper extends AbstractCASProtocolMapper { ...@@ -54,7 +56,8 @@ public class UserSessionNoteMapper extends AbstractCASProtocolMapper {
} }
@Override @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 noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE);
String noteValue = userSession.getNote(noteName); String noteValue = userSession.getNote(noteName);
if (noteValue == null) return; if (noteValue == null) return;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment