Skip to content
This repository has been archived by the owner on Oct 12, 2020. It is now read-only.

Commit

Permalink
update for KEYCLOAK-6884 KEYCLOAK-3454 KEYCLOAK-8298
Browse files Browse the repository at this point in the history
  • Loading branch information
Doccrazy committed Nov 16, 2018
1 parent f63e407 commit 89e3d75
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protected Response successResponse() {
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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -33,66 +30,24 @@
*/
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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -41,7 +39,8 @@ public String getHelpText() {
}

@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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -52,7 +50,8 @@ public String getHelpText() {
}

@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()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -53,7 +55,8 @@ public String getHelpText() {
}

@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));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -66,7 +64,8 @@ public String getHelpText() {
}

@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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -60,40 +61,25 @@ public String getHelpText() {
}

@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;
}

RealmModel clientRealm = userSession.getRealm();
ClientModel client = clientRealm.getClientByClientId(clientId.trim());

if (client == 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);
}

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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -50,7 +48,8 @@ public String getHelpText() {
}

@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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -53,9 +57,16 @@ public String getHelpText() {
}

@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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -54,7 +56,8 @@ public String getHelpText() {
}

@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;
Expand Down

0 comments on commit 89e3d75

Please sign in to comment.