KEYCLOAK-15889 Streamification of ProtocolMappers

This commit is contained in:
Martin Kanis 2020-10-09 11:18:11 +02:00 committed by Hynek Mlnařík
parent e6bd12b174
commit d9029b06b9
32 changed files with 363 additions and 431 deletions

View file

@ -357,9 +357,9 @@ public class ClientAdapter implements ClientModel, CachedObject {
} }
@Override @Override
public Set<ProtocolMapperModel> getProtocolMappers() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
if (isUpdated()) return updated.getProtocolMappers(); if (isUpdated()) return updated.getProtocolMappersStream();
return cached.getProtocolMappers(); return cached.getProtocolMappers().stream();
} }
@Override @Override

View file

@ -26,7 +26,6 @@ import org.keycloak.models.utils.RoleUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@ -79,9 +78,9 @@ public class ClientScopeAdapter implements ClientScopeModel {
} }
@Override @Override
public Set<ProtocolMapperModel> getProtocolMappers() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
if (isUpdated()) return updated.getProtocolMappers(); if (isUpdated()) return updated.getProtocolMappersStream();
return cached.getProtocolMappers(); return cached.getProtocolMappers().stream();
} }
@Override @Override

View file

@ -94,9 +94,7 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
redirectUris.addAll(model.getRedirectUris()); redirectUris.addAll(model.getRedirectUris());
webOrigins.addAll(model.getWebOrigins()); webOrigins.addAll(model.getWebOrigins());
scope.addAll(model.getScopeMappingsStream().map(RoleModel::getId).collect(Collectors.toSet())); scope.addAll(model.getScopeMappingsStream().map(RoleModel::getId).collect(Collectors.toSet()));
for (ProtocolMapperModel mapper : model.getProtocolMappers()) { protocolMappers.addAll(model.getProtocolMappersStream().collect(Collectors.toSet()));
this.protocolMappers.add(mapper);
}
surrogateAuthRequired = model.isSurrogateAuthRequired(); surrogateAuthRequired = model.isSurrogateAuthRequired();
managementUrl = model.getManagementUrl(); managementUrl = model.getManagementUrl();
rootUrl = model.getRootUrl(); rootUrl = model.getRootUrl();

View file

@ -48,9 +48,7 @@ public class CachedClientScope extends AbstractRevisioned implements InRealm {
description = model.getDescription(); description = model.getDescription();
this.realm = realm.getId(); this.realm = realm.getId();
protocol = model.getProtocol(); protocol = model.getProtocol();
for (ProtocolMapperModel mapper : model.getProtocolMappers()) { protocolMappers.addAll(model.getProtocolMappersStream().collect(Collectors.toSet()));
this.protocolMappers.add(mapper);
}
scope.addAll(model.getScopeMappingsStream().map(RoleModel::getId).collect(Collectors.toSet())); scope.addAll(model.getScopeMappingsStream().map(RoleModel::getId).collect(Collectors.toSet()));
attributes.putAll(model.getAttributes()); attributes.putAll(model.getAttributes());
} }

View file

@ -400,22 +400,22 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
} }
@Override @Override
public Set<ProtocolMapperModel> getProtocolMappers() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
Set<ProtocolMapperModel> mappings = new HashSet<ProtocolMapperModel>(); return this.entity.getProtocolMappers().stream()
for (ProtocolMapperEntity entity : this.entity.getProtocolMappers()) { .map(entity -> {
ProtocolMapperModel mapping = new ProtocolMapperModel(); ProtocolMapperModel mapping = new ProtocolMapperModel();
mapping.setId(entity.getId()); mapping.setId(entity.getId());
mapping.setName(entity.getName()); mapping.setName(entity.getName());
mapping.setProtocol(entity.getProtocol()); mapping.setProtocol(entity.getProtocol());
mapping.setProtocolMapper(entity.getProtocolMapper()); mapping.setProtocolMapper(entity.getProtocolMapper());
Map<String, String> config = new HashMap<String, String>(); Map<String, String> config = new HashMap<>();
if (entity.getConfig() != null) { if (entity.getConfig() != null) {
config.putAll(entity.getConfig()); config.putAll(entity.getConfig());
} }
mapping.setConfig(config); mapping.setConfig(config);
mappings.add(mapping); return mapping;
} })
return mappings; .distinct();
} }
@Override @Override

View file

@ -32,11 +32,9 @@ import org.keycloak.models.utils.RoleUtils;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@ -100,22 +98,22 @@ public class ClientScopeAdapter implements ClientScopeModel, JpaModel<ClientScop
} }
@Override @Override
public Set<ProtocolMapperModel> getProtocolMappers() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
Set<ProtocolMapperModel> mappings = new HashSet<ProtocolMapperModel>(); return this.entity.getProtocolMappers().stream()
for (ProtocolMapperEntity entity : this.entity.getProtocolMappers()) { .map(entity -> {
ProtocolMapperModel mapping = new ProtocolMapperModel(); ProtocolMapperModel mapping = new ProtocolMapperModel();
mapping.setId(entity.getId()); mapping.setId(entity.getId());
mapping.setName(entity.getName()); mapping.setName(entity.getName());
mapping.setProtocol(entity.getProtocol()); mapping.setProtocol(entity.getProtocol());
mapping.setProtocolMapper(entity.getProtocolMapper()); mapping.setProtocolMapper(entity.getProtocolMapper());
Map<String, String> config = new HashMap<String, String>(); Map<String, String> config = new HashMap<>();
if (entity.getConfig() != null) { if (entity.getConfig() != null) {
config.putAll(entity.getConfig()); config.putAll(entity.getConfig());
} }
mapping.setConfig(config); mapping.setConfig(config);
mappings.add(mapping); return mapping;
} })
return mappings; .distinct();
} }
@Override @Override

View file

@ -26,10 +26,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import com.google.common.base.Functions; import com.google.common.base.Functions;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -478,8 +475,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
/*************** Protocol mappers ****************/ /*************** Protocol mappers ****************/
@Override @Override
public Set<ProtocolMapperModel> getProtocolMappers() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
return Collections.unmodifiableSet(new HashSet<>(entity.getProtocolMappers())); return entity.getProtocolMappers().stream().distinct();
} }
@Override @Override

View file

@ -17,9 +17,6 @@
package org.keycloak.migration.migrators; package org.keycloak.migration.migrators;
import java.util.LinkedList;
import java.util.List;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
@ -29,7 +26,6 @@ import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperContainerModel; import org.keycloak.models.ProtocolMapperContainerModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel; import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
@ -71,17 +67,10 @@ public class MigrationUtils {
} }
public static void updateProtocolMappers(ProtocolMapperContainerModel client) { public static void updateProtocolMappers(ProtocolMapperContainerModel client) {
List<ProtocolMapperModel> toUpdate = new LinkedList<>(); client.getProtocolMappersStream()
for (ProtocolMapperModel mapper : client.getProtocolMappers()) { .filter(mapper -> !mapper.getConfig().containsKey("userinfo.token.claim") && mapper.getConfig().containsKey("id.token.claim"))
if (!mapper.getConfig().containsKey("userinfo.token.claim") && mapper.getConfig().containsKey("id.token.claim")) { .peek(mapper -> mapper.getConfig().put("userinfo.token.claim", mapper.getConfig().get("id.token.claim")))
mapper.getConfig().put("userinfo.token.claim", mapper.getConfig().get("id.token.claim")); .forEach(client::updateProtocolMapper);
toUpdate.add(mapper);
}
}
for (ProtocolMapperModel mapper : toUpdate) {
client.updateProtocolMapper(mapper);
}
} }

View file

@ -555,13 +555,10 @@ public class ModelToRepresentation {
rep.setName(clientScopeModel.getName()); rep.setName(clientScopeModel.getName());
rep.setDescription(clientScopeModel.getDescription()); rep.setDescription(clientScopeModel.getDescription());
rep.setProtocol(clientScopeModel.getProtocol()); rep.setProtocol(clientScopeModel.getProtocol());
if (!clientScopeModel.getProtocolMappers().isEmpty()) { List<ProtocolMapperRepresentation> mappings = clientScopeModel.getProtocolMappersStream()
List<ProtocolMapperRepresentation> mappings = new LinkedList<>(); .map(ModelToRepresentation::toRepresentation).collect(Collectors.toList());
for (ProtocolMapperModel model : clientScopeModel.getProtocolMappers()) { if (!mappings.isEmpty())
mappings.add(toRepresentation(model));
}
rep.setProtocolMappers(mappings); rep.setProtocolMappers(mappings);
}
rep.setAttributes(new HashMap<>(clientScopeModel.getAttributes())); rep.setAttributes(new HashMap<>(clientScopeModel.getAttributes()));
@ -621,13 +618,10 @@ public class ModelToRepresentation {
rep.setRegisteredNodes(new HashMap<>(clientModel.getRegisteredNodes())); rep.setRegisteredNodes(new HashMap<>(clientModel.getRegisteredNodes()));
} }
if (!clientModel.getProtocolMappers().isEmpty()) { List<ProtocolMapperRepresentation> mappings = clientModel.getProtocolMappersStream()
List<ProtocolMapperRepresentation> mappings = new LinkedList<>(); .map(ModelToRepresentation::toRepresentation).collect(Collectors.toList());
for (ProtocolMapperModel model : clientModel.getProtocolMappers()) { if (!mappings.isEmpty())
mappings.add(toRepresentation(model));
}
rep.setProtocolMappers(mappings); rep.setProtocolMappers(mappings);
}
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class); AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
ResourceServer resourceServer = authorization.getStoreFactory().getResourceServerStore().findById(clientModel.getId()); ResourceServer resourceServer = authorization.getStoreFactory().getResourceServerStore().findById(clientModel.getId());

View file

@ -1414,8 +1414,7 @@ public class RepresentationToModel {
if (resourceRep.getProtocolMappers() != null) { if (resourceRep.getProtocolMappers() != null) {
// first, remove all default/built in mappers // first, remove all default/built in mappers
Set<ProtocolMapperModel> mappers = client.getProtocolMappers(); client.getProtocolMappersStream().collect(Collectors.toList()).forEach(client::removeProtocolMapper);
for (ProtocolMapperModel mapper : mappers) client.removeProtocolMapper(mapper);
for (ProtocolMapperRepresentation mapper : resourceRep.getProtocolMappers()) { for (ProtocolMapperRepresentation mapper : resourceRep.getProtocolMappers()) {
client.addProtocolMapper(toModel(mapper)); client.addProtocolMapper(toModel(mapper));
@ -1556,10 +1555,10 @@ public class RepresentationToModel {
public static void updateClientProtocolMappers(ClientRepresentation rep, ClientModel resource) { public static void updateClientProtocolMappers(ClientRepresentation rep, ClientModel resource) {
if (rep.getProtocolMappers() != null) { if (rep.getProtocolMappers() != null) {
Map<String,ProtocolMapperModel> existingProtocolMappers = new HashMap<>(); Map<String,ProtocolMapperModel> existingProtocolMappers =
for (ProtocolMapperModel existingProtocolMapper : resource.getProtocolMappers()) { resource.getProtocolMappersStream().collect(Collectors.toMap(mapper ->
existingProtocolMappers.put(generateProtocolNameKey(existingProtocolMapper.getProtocol(), existingProtocolMapper.getName()), existingProtocolMapper); generateProtocolNameKey(mapper.getProtocol(), mapper.getName()), Function.identity()));
}
for (ProtocolMapperRepresentation protocolMapperRepresentation : rep.getProtocolMappers()) { for (ProtocolMapperRepresentation protocolMapperRepresentation : rep.getProtocolMappers()) {
String protocolNameKey = generateProtocolNameKey(protocolMapperRepresentation.getProtocol(), protocolMapperRepresentation.getName()); String protocolNameKey = generateProtocolNameKey(protocolMapperRepresentation.getProtocol(), protocolMapperRepresentation.getName());
@ -1606,8 +1605,7 @@ public class RepresentationToModel {
if (resourceRep.getProtocol() != null) clientScope.setProtocol(resourceRep.getProtocol()); if (resourceRep.getProtocol() != null) clientScope.setProtocol(resourceRep.getProtocol());
if (resourceRep.getProtocolMappers() != null) { if (resourceRep.getProtocolMappers() != null) {
// first, remove all default/built in mappers // first, remove all default/built in mappers
Set<ProtocolMapperModel> mappers = clientScope.getProtocolMappers(); clientScope.getProtocolMappersStream().collect(Collectors.toList()).forEach(clientScope::removeProtocolMapper);
for (ProtocolMapperModel mapper : mappers) clientScope.removeProtocolMapper(mapper);
for (ProtocolMapperRepresentation mapper : resourceRep.getProtocolMappers()) { for (ProtocolMapperRepresentation mapper : resourceRep.getProtocolMappers()) {
clientScope.addProtocolMapper(toModel(mapper)); clientScope.addProtocolMapper(toModel(mapper));

View file

@ -23,7 +23,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.storage.ReadOnlyException; import org.keycloak.storage.ReadOnlyException;
import java.util.List;
import java.util.Set; import java.util.Set;
/** /**

View file

@ -32,7 +32,20 @@ public interface ClientSessionContext {
Set<String> getClientScopeIds(); Set<String> getClientScopeIds();
Set<ClientScopeModel> getClientScopes(); /**
* @deprecated Use {@link #getClientScopesStream() getClientScopesStream} instead.
* @return Set of protocol mappers
*/
@Deprecated
default Set<ClientScopeModel> getClientScopes() {
return getClientScopesStream().collect(Collectors.toSet());
}
/**
* Returns client scopes as a stream.
* @return Stream of client scopes.
*/
Stream<ClientScopeModel> getClientScopesStream();
/** /**
* @return expanded roles (composite roles already applied) * @return expanded roles (composite roles already applied)
@ -44,7 +57,20 @@ public interface ClientSessionContext {
Stream<RoleModel> getRolesStream(); Stream<RoleModel> getRolesStream();
Set<ProtocolMapperModel> getProtocolMappers(); /**
* @deprecated Use {@link #getProtocolMappersStream() getProtocolMappersStream} instead.
* @return Set of protocol mappers
*/
@Deprecated
default Set<ProtocolMapperModel> getProtocolMappers() {
return getProtocolMappersStream().collect(Collectors.toSet());
}
/**
* Returns protocol mappers as a stream.
* @return Stream of protocol mappers.
*/
Stream<ProtocolMapperModel> getProtocolMappersStream();
String getScopeString(); String getScopeString();

View file

@ -18,13 +18,28 @@
package org.keycloak.models; package org.keycloak.models;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface ProtocolMapperContainerModel { public interface ProtocolMapperContainerModel {
Set<ProtocolMapperModel> getProtocolMappers(); /**
* @deprecated Use {@link #getProtocolMappersStream() getProtocolMappersStream} instead.
* @return Set of protocol mappers
*/
@Deprecated
default Set<ProtocolMapperModel> getProtocolMappers() {
return getProtocolMappersStream().collect(Collectors.toSet());
}
/**
* Returns protocol mappers as a stream.
* @return Stream of protocol mapper.
*/
Stream<ProtocolMapperModel> getProtocolMappersStream();
ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model); ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model);

View file

@ -40,6 +40,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -57,9 +58,10 @@ public class ApplicationsBean {
} }
// Construct scope parameter with all optional scopes to see all potentially available roles // Construct scope parameter with all optional scopes to see all potentially available roles
Set<ClientScopeModel> allClientScopes = new HashSet<>(client.getClientScopes(true, true).values()); Stream<ClientScopeModel> allClientScopes = Stream.concat(
allClientScopes.addAll(client.getClientScopes(false, true).values()); client.getClientScopes(true, true).values().stream(),
allClientScopes.add(client); client.getClientScopes(false, true).values().stream());
allClientScopes = Stream.concat(allClientScopes, Stream.of(client)).distinct();
Set<RoleModel> availableRoles = TokenManager.getAccess(user, client, allClientScopes); Set<RoleModel> availableRoles = TokenManager.getAccess(user, client, allClientScopes);

View file

@ -29,10 +29,10 @@ import org.keycloak.provider.ProviderFactory;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Map.Entry;
import java.util.stream.Collectors; import java.util.Objects;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -123,27 +123,22 @@ public class ProtocolMapperUtils {
} }
public static List<Map.Entry<ProtocolMapperModel, ProtocolMapper>> getSortedProtocolMappers(KeycloakSession session, ClientSessionContext ctx) { public static Stream<Entry<ProtocolMapperModel, ProtocolMapper>> getSortedProtocolMappers(KeycloakSession session, ClientSessionContext ctx) {
Set<ProtocolMapperModel> mapperModels = ctx.getProtocolMappers();
Map<ProtocolMapperModel, ProtocolMapper> result = new HashMap<>();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory(); KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapperModel : mapperModels) { return ctx.getProtocolMappersStream()
ProtocolMapper mapper = (ProtocolMapper) sessionFactory.getProviderFactory(ProtocolMapper.class, mapperModel.getProtocolMapper()); .flatMap(mapperModel -> {
if (mapper == null) { ProtocolMapper mapper = (ProtocolMapper) sessionFactory.getProviderFactory(ProtocolMapper.class, mapperModel.getProtocolMapper());
continue; if (mapper == null)
} return null;
Map<ProtocolMapperModel, ProtocolMapper> protocolMapperMap = new HashMap<>();
result.put(mapperModel, mapper); protocolMapperMap.put(mapperModel, mapper);
} return protocolMapperMap.entrySet().stream();
})
return result.entrySet() .filter(Objects::nonNull)
.stream() .sorted(Comparator.comparing(ProtocolMapperUtils::compare));
.sorted(Comparator.comparing(ProtocolMapperUtils::compare))
.collect(Collectors.toList());
} }
public static int compare(Map.Entry<ProtocolMapperModel, ProtocolMapper> entry) { public static int compare(Entry<ProtocolMapperModel, ProtocolMapper> entry) {
int priority = entry.getValue().getPriority(); int priority = entry.getValue().getPriority();
return priority; return priority;
} }

View file

@ -10,12 +10,10 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionContext; import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeyManager; import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.docker.mapper.DockerAuthV2AttributeMapper; import org.keycloak.protocol.docker.mapper.DockerAuthV2AttributeMapper;
import org.keycloak.representations.docker.DockerResponse; import org.keycloak.representations.docker.DockerResponse;
@ -30,7 +28,7 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.concurrent.atomic.AtomicReference;
public class DockerAuthV2Protocol implements LoginProtocol { public class DockerAuthV2Protocol implements LoginProtocol {
protected static final Logger logger = Logger.getLogger(DockerEndpoint.class); protected static final Logger logger = Logger.getLogger(DockerEndpoint.class);
@ -111,17 +109,13 @@ public class DockerAuthV2Protocol implements LoginProtocol {
// Next, allow mappers to decorate the token to add/remove scopes as appropriate // Next, allow mappers to decorate the token to add/remove scopes as appropriate
for (Map.Entry<ProtocolMapperModel, ProtocolMapper> entry : ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)) { AtomicReference<DockerResponseToken> finalResponseToken = new AtomicReference<>(responseToken);
ProtocolMapperModel mapping = entry.getKey(); ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)
ProtocolMapper mapper = entry.getValue(); .filter(mapper -> mapper.getValue() instanceof DockerAuthV2AttributeMapper)
.filter(mapper -> ((DockerAuthV2AttributeMapper) mapper.getValue()).appliesTo(finalResponseToken.get()))
if (mapper instanceof DockerAuthV2AttributeMapper) { .forEach(mapper -> finalResponseToken.set(((DockerAuthV2AttributeMapper) mapper.getValue())
final DockerAuthV2AttributeMapper dockerAttributeMapper = (DockerAuthV2AttributeMapper) mapper; .transformDockerResponseToken(finalResponseToken.get(), mapper.getKey(), session, userSession, clientSession)));
if (dockerAttributeMapper.appliesTo(responseToken)) { responseToken = finalResponseToken.get();
responseToken = dockerAttributeMapper.transformDockerResponseToken(responseToken, mapping, session, userSession, clientSession);
}
}
}
try { try {
// Finally, construct the response to the docker client with the token + metadata // Finally, construct the response to the docker client with the token + metadata

View file

@ -43,7 +43,6 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel; import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ClientSessionContext; import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.TokenRevocationStoreProvider; import org.keycloak.models.TokenRevocationStoreProvider;
@ -53,7 +52,6 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider; import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RoleUtils; import org.keycloak.models.utils.RoleUtils;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper; import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper;
@ -78,11 +76,13 @@ import org.keycloak.util.TokenUtil;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -199,7 +199,7 @@ public class TokenManager {
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, oldTokenScope, session); ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, oldTokenScope, session);
// Check user didn't revoke granted consent // Check user didn't revoke granted consent
if (!verifyConsentStillAvailable(session, user, client, clientSessionCtx.getClientScopes())) { if (!verifyConsentStillAvailable(session, user, client, clientSessionCtx.getClientScopesStream())) {
throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user"); throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user");
} }
@ -511,7 +511,7 @@ public class TokenManager {
} }
public static Set<RoleModel> getAccess(UserModel user, ClientModel client, Set<ClientScopeModel> clientScopes) { public static Set<RoleModel> getAccess(UserModel user, ClientModel client, Stream<ClientScopeModel> clientScopes) {
Set<RoleModel> roleMappings = RoleUtils.getDeepUserRoleMappings(user); Set<RoleModel> roleMappings = RoleUtils.getDeepUserRoleMappings(user);
if (client.isFullScopeAllowed()) { if (client.isFullScopeAllowed()) {
@ -525,12 +525,17 @@ public class TokenManager {
Stream<RoleModel> scopeMappings = client.getRolesStream(); Stream<RoleModel> scopeMappings = client.getRolesStream();
// 2 - Role mappings of client itself + default client scopes + optional client scopes requested by scope parameter (if applyScopeParam is true) // 2 - Role mappings of client itself + default client scopes + optional client scopes requested by scope parameter (if applyScopeParam is true)
for (ClientScopeModel clientScope : clientScopes) { Stream<RoleModel> clientScopesMappings;
if (logger.isTraceEnabled()) { if (!logger.isTraceEnabled()) {
logger.tracef("Adding client scope role mappings of client scope '%s' to client '%s'", clientScope.getName(), client.getClientId()); clientScopesMappings = clientScopes.flatMap(clientScope -> clientScope.getScopeMappingsStream());
} } else {
scopeMappings = Stream.concat(scopeMappings, clientScope.getScopeMappingsStream()); clientScopesMappings = clientScopes.flatMap(clientScope -> {
logger.tracef("Adding client scope role mappings of client scope '%s' to client '%s'",
clientScope.getName(), client.getClientId());
return clientScope.getScopeMappingsStream();
});
} }
scopeMappings = Stream.concat(scopeMappings, clientScopesMappings);
// 3 - Expand scope mappings // 3 - Expand scope mappings
scopeMappings = RoleUtils.expandCompositeRolesStream(scopeMappings); scopeMappings = RoleUtils.expandCompositeRolesStream(scopeMappings);
@ -544,28 +549,20 @@ public class TokenManager {
/** Return client itself + all default client scopes of client + optional client scopes requested by scope parameter **/ /** Return client itself + all default client scopes of client + optional client scopes requested by scope parameter **/
public static Set<ClientScopeModel> getRequestedClientScopes(String scopeParam, ClientModel client) { public static Stream<ClientScopeModel> getRequestedClientScopes(String scopeParam, ClientModel client) {
// Add all default client scopes automatically // Add all default client scopes automatically and client itself
Set<ClientScopeModel> clientScopes = new HashSet<>(client.getClientScopes(true, true).values()); Stream<ClientScopeModel> clientScopes = Stream.concat(
client.getClientScopes(true, true).values().stream(),
// Add client itself Stream.of(client)).distinct();
clientScopes.add(client);
if (scopeParam == null) { if (scopeParam == null) {
return clientScopes; return clientScopes;
} }
// Add optional client scopes requested by scope parameter
Collection<String> scopeParamParts = parseScopeParameter(scopeParam);
Map<String, ClientScopeModel> allOptionalScopes = client.getClientScopes(false, true); Map<String, ClientScopeModel> allOptionalScopes = client.getClientScopes(false, true);
for (String scopeParamPart : scopeParamParts) { // Add optional client scopes requested by scope parameter
ClientScopeModel scope = allOptionalScopes.get(scopeParamPart); return Stream.concat(parseScopeParameter(scopeParam).map(allOptionalScopes::get).filter(Objects::nonNull),
if (scope != null) { clientScopes).distinct();
clientScopes.add(scope);
}
}
return clientScopes;
} }
public static boolean isValidScope(String scopes, ClientModel client) { public static boolean isValidScope(String scopes, ClientModel client) {
@ -573,11 +570,11 @@ public class TokenManager {
return true; return true;
} }
Set<String> clientScopes = getRequestedClientScopes(scopes, client).stream() Set<String> clientScopes = getRequestedClientScopes(scopes, client)
.filter(obj -> !ClientModel.class.isInstance(obj)) .filter(((Predicate<ClientScopeModel>) ClientModel.class::isInstance).negate())
.map(ClientScopeModel::getName) .map(ClientScopeModel::getName)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
Collection<String> requestedScopes = TokenManager.parseScopeParameter(scopes); Collection<String> requestedScopes = TokenManager.parseScopeParameter(scopes).collect(Collectors.toSet());
if (TokenUtil.isOIDCRequest(scopes)) { if (TokenUtil.isOIDCRequest(scopes)) {
requestedScopes.remove(OAuth2Constants.SCOPE_OPENID); requestedScopes.remove(OAuth2Constants.SCOPE_OPENID);
@ -597,73 +594,61 @@ public class TokenManager {
return true; return true;
} }
public static Collection<String> parseScopeParameter(String scopeParam) { public static Stream<String> parseScopeParameter(String scopeParam) {
return new HashSet<>(Arrays.asList(scopeParam.split(" "))); return Arrays.stream(scopeParam.split(" ")).distinct();
} }
// Check if user still has granted consents to all requested client scopes // Check if user still has granted consents to all requested client scopes
public static boolean verifyConsentStillAvailable(KeycloakSession session, UserModel user, ClientModel client, Set<ClientScopeModel> requestedClientScopes) { public static boolean verifyConsentStillAvailable(KeycloakSession session, UserModel user, ClientModel client,
Stream<ClientScopeModel> requestedClientScopes) {
if (!client.isConsentRequired()) { if (!client.isConsentRequired()) {
return true; return true;
} }
UserConsentModel grantedConsent = session.users().getConsentByClient(client.getRealm(), user.getId(), client.getId()); UserConsentModel grantedConsent = session.users().getConsentByClient(client.getRealm(), user.getId(), client.getId());
for (ClientScopeModel requestedScope : requestedClientScopes) { return requestedClientScopes
if (!requestedScope.isDisplayOnConsentScreen()) { .filter(ClientScopeModel::isDisplayOnConsentScreen)
continue; .noneMatch(requestedScope -> {
} if (grantedConsent == null || !grantedConsent.getGrantedClientScopes().contains(requestedScope)) {
logger.debugf("Client '%s' no longer has requested consent from user '%s' for client scope '%s'",
if (grantedConsent == null || !grantedConsent.getGrantedClientScopes().contains(requestedScope)) { client.getClientId(), user.getUsername(), requestedScope.getName());
logger.debugf("Client '%s' no longer has requested consent from user '%s' for client scope '%s'", return true;
client.getClientId(), user.getUsername(), requestedScope.getName()); }
return false; return false;
} });
}
return true;
} }
public AccessToken transformAccessToken(KeycloakSession session, AccessToken token, public AccessToken transformAccessToken(KeycloakSession session, AccessToken token,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) { UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
for (Map.Entry<ProtocolMapperModel, ProtocolMapper> entry : ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)) { AtomicReference<AccessToken> finalToken = new AtomicReference<>(token);
ProtocolMapperModel mapping = entry.getKey(); ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)
ProtocolMapper mapper = entry.getValue(); .filter(mapper -> mapper.getValue() instanceof OIDCAccessTokenMapper)
if (mapper instanceof OIDCAccessTokenMapper) { .forEach(mapper -> finalToken.set(((OIDCAccessTokenMapper) mapper.getValue())
token = ((OIDCAccessTokenMapper) mapper).transformAccessToken(token, mapping, session, userSession, clientSessionCtx); .transformAccessToken(finalToken.get(), mapper.getKey(), session, userSession, clientSessionCtx)));
} return finalToken.get();
}
return token;
} }
public AccessToken transformUserInfoAccessToken(KeycloakSession session, AccessToken token, public AccessToken transformUserInfoAccessToken(KeycloakSession session, AccessToken token,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) { UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
for (Map.Entry<ProtocolMapperModel, ProtocolMapper> entry : ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)) { AtomicReference<AccessToken> finalToken = new AtomicReference<>(token);
ProtocolMapperModel mapping = entry.getKey(); ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)
ProtocolMapper mapper = entry.getValue(); .filter(mapper -> mapper.getValue() instanceof UserInfoTokenMapper)
.forEach(mapper -> finalToken.set(((UserInfoTokenMapper) mapper.getValue())
if (mapper instanceof UserInfoTokenMapper) { .transformUserInfoToken(finalToken.get(), mapper.getKey(), session, userSession, clientSessionCtx)));
token = ((UserInfoTokenMapper) mapper).transformUserInfoToken(token, mapping, session, userSession, clientSessionCtx); return finalToken.get();
}
}
return token;
} }
public void transformIDToken(KeycloakSession session, IDToken token, public void transformIDToken(KeycloakSession session, IDToken token,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) { UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
for (Map.Entry<ProtocolMapperModel, ProtocolMapper> entry : ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)) { AtomicReference<IDToken> finalToken = new AtomicReference<>(token);
ProtocolMapperModel mapping = entry.getKey(); ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)
ProtocolMapper mapper = entry.getValue(); .filter(mapper -> mapper.getValue() instanceof OIDCIDTokenMapper)
.forEach(mapper -> finalToken.set(((OIDCIDTokenMapper) mapper.getValue())
if (mapper instanceof OIDCIDTokenMapper) { .transformIDToken(finalToken.get(), mapper.getKey(), session, userSession, clientSessionCtx)));
token = ((OIDCIDTokenMapper) mapper).transformIDToken(token, mapping, session, userSession, clientSessionCtx);
}
}
} }
protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session,

View file

@ -127,9 +127,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID; import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID;
@ -420,13 +422,13 @@ public class TokenEndpoint {
// Compute client scopes again from scope parameter. Check if user still has them granted // Compute client scopes again from scope parameter. Check if user still has them granted
// (but in code-to-token request, it could just theoretically happen that they are not available) // (but in code-to-token request, it could just theoretically happen that they are not available)
String scopeParam = codeData.getScope(); String scopeParam = codeData.getScope();
Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client); Supplier<Stream<ClientScopeModel>> clientScopesSupplier = () -> TokenManager.getRequestedClientScopes(scopeParam, client);
if (!TokenManager.verifyConsentStillAvailable(session, user, client, clientScopes)) { if (!TokenManager.verifyConsentStillAvailable(session, user, client, clientScopesSupplier.get())) {
event.error(Errors.NOT_ALLOWED); event.error(Errors.NOT_ALLOWED);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user", Response.Status.BAD_REQUEST); throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user", Response.Status.BAD_REQUEST);
} }
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndClientScopes(clientSession, clientScopes, session); ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndClientScopes(clientSession, clientScopesSupplier.get(), session);
// Set nonce as an attribute in the ClientSessionContext. Will be used for the token generation // Set nonce as an attribute in the ClientSessionContext. Will be used for the token generation
clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, codeData.getNonce()); clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, codeData.getNonce());

View file

@ -25,7 +25,6 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.protocol.ClientInstallationProvider; import org.keycloak.protocol.ClientInstallationProvider;
@ -41,6 +40,7 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -110,15 +110,11 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
// Check if there is client scope with audience protocol mapper created for particular client. If yes, admin wants verifying token audience // Check if there is client scope with audience protocol mapper created for particular client. If yes, admin wants verifying token audience
String clientId = client.getClientId(); String clientId = client.getClientId();
return client.getRealm().getClientScopesStream().anyMatch(clientScope -> { return client.getRealm().getClientScopesStream().anyMatch(clientScope ->
for (ProtocolMapperModel protocolMapper : clientScope.getProtocolMappers()) { clientScope.getProtocolMappersStream().anyMatch(protocolMapper ->
if (AudienceProtocolMapper.PROVIDER_ID.equals(protocolMapper.getProtocolMapper()) && Objects.equals(protocolMapper.getProtocolMapper(), AudienceProtocolMapper.PROVIDER_ID) &&
(clientId.equals(protocolMapper.getConfig().get(AudienceProtocolMapper.INCLUDED_CLIENT_AUDIENCE)))) { Objects.equals(clientId, protocolMapper.getConfig().get(AudienceProtocolMapper.INCLUDED_CLIENT_AUDIENCE)))
return true; );
}
}
return false;
});
} }

View file

@ -88,6 +88,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -420,22 +421,23 @@ public class SamlProtocol implements LoginProtocol {
List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers = new LinkedList<>(); List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers = new LinkedList<>();
List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> loginResponseMappers = new LinkedList<>(); List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> loginResponseMappers = new LinkedList<>();
ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper = null; AtomicReference<ProtocolMapperProcessor<SAMLRoleListMapper>> roleListMapper = new AtomicReference<>(null);
for (Map.Entry<ProtocolMapperModel, ProtocolMapper> entry : ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)) { ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)
ProtocolMapperModel mapping = entry.getKey(); .forEach(entry -> {
ProtocolMapper mapper = entry.getValue(); ProtocolMapperModel mapping = entry.getKey();
ProtocolMapper mapper = entry.getValue();
if (mapper instanceof SAMLAttributeStatementMapper) { if (mapper instanceof SAMLAttributeStatementMapper) {
attributeStatementMappers.add(new ProtocolMapperProcessor<SAMLAttributeStatementMapper>((SAMLAttributeStatementMapper) mapper, mapping)); attributeStatementMappers.add(new ProtocolMapperProcessor<>((SAMLAttributeStatementMapper) mapper, mapping));
} }
if (mapper instanceof SAMLLoginResponseMapper) { if (mapper instanceof SAMLLoginResponseMapper) {
loginResponseMappers.add(new ProtocolMapperProcessor<SAMLLoginResponseMapper>((SAMLLoginResponseMapper) mapper, mapping)); loginResponseMappers.add(new ProtocolMapperProcessor<>((SAMLLoginResponseMapper) mapper, mapping));
} }
if (mapper instanceof SAMLRoleListMapper) { if (mapper instanceof SAMLRoleListMapper) {
roleListMapper = new ProtocolMapperProcessor<SAMLRoleListMapper>((SAMLRoleListMapper) mapper, mapping); roleListMapper.set(new ProtocolMapperProcessor<>((SAMLRoleListMapper) mapper, mapping));
} }
} });
Document samlDocument = null; Document samlDocument = null;
KeyManager keyManager = session.keys(); KeyManager keyManager = session.keys();
@ -450,7 +452,7 @@ public class SamlProtocol implements LoginProtocol {
ResponseType samlModel = builder.buildModel(); ResponseType samlModel = builder.buildModel();
final AttributeStatementType attributeStatement = populateAttributeStatements(attributeStatementMappers, session, userSession, clientSession); final AttributeStatementType attributeStatement = populateAttributeStatements(attributeStatementMappers, session, userSession, clientSession);
populateRoles(roleListMapper, session, userSession, clientSessionCtx, attributeStatement); populateRoles(roleListMapper.get(), session, userSession, clientSessionCtx, attributeStatement);
// SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute // SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
if (attributeStatement.getAttributes().size() > 0) { if (attributeStatement.getAttributes().size() > 0) {

View file

@ -34,6 +34,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -112,32 +113,34 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo
boolean singleAttribute = Boolean.parseBoolean(single); boolean singleAttribute = Boolean.parseBoolean(single);
List<SamlProtocol.ProtocolMapperProcessor<SAMLRoleNameMapper>> roleNameMappers = new LinkedList<>(); List<SamlProtocol.ProtocolMapperProcessor<SAMLRoleNameMapper>> roleNameMappers = new LinkedList<>();
AttributeType singleAttributeType = null; AtomicReference<AttributeType> singleAttributeType = new AtomicReference<>(null);
for (Map.Entry<ProtocolMapperModel, ProtocolMapper> entry : ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)) { ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx)
ProtocolMapperModel mapping = entry.getKey(); .forEach(entry -> {
ProtocolMapper mapper = entry.getValue(); ProtocolMapperModel mapping = entry.getKey();
ProtocolMapper mapper = entry.getValue();
if (mapper instanceof SAMLRoleNameMapper) { if (mapper instanceof SAMLRoleNameMapper) {
roleNameMappers.add(new SamlProtocol.ProtocolMapperProcessor<>((SAMLRoleNameMapper) mapper,mapping)); roleNameMappers.add(new SamlProtocol.ProtocolMapperProcessor<>((SAMLRoleNameMapper) mapper,mapping));
}
if (mapper instanceof HardcodedRole) {
AttributeType attributeType;
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(HardcodedRole.ROLE_ATTRIBUTE)); if (mapper instanceof HardcodedRole) {
} AttributeType attributeType;
} if (singleAttribute) {
if (singleAttributeType.get() == null) {
singleAttributeType.set(AttributeStatementHelper.createAttributeType(mappingModel));
roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(singleAttributeType.get()));
}
attributeType = singleAttributeType.get();
} else {
attributeType = AttributeStatementHelper.createAttributeType(mappingModel);
roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType));
}
attributeType.addAttributeValue(mapping.getConfig().get(HardcodedRole.ROLE_ATTRIBUTE));
}
});
List<String> allRoleNames = clientSessionCtx.getRolesStream() List<String> allRoleNames = clientSessionCtx.getRolesStream()
// todo need a role mapping // todo need a role mapping
@ -151,11 +154,11 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo
for (String roleName : allRoleNames) { for (String roleName : allRoleNames) {
AttributeType attributeType; AttributeType attributeType;
if (singleAttribute) { if (singleAttribute) {
if (singleAttributeType == null) { if (singleAttributeType.get() == null) {
singleAttributeType = AttributeStatementHelper.createAttributeType(mappingModel); singleAttributeType.set(AttributeStatementHelper.createAttributeType(mappingModel));
roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(singleAttributeType)); roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(singleAttributeType.get()));
} }
attributeType = singleAttributeType; attributeType = singleAttributeType.get();
} else { } else {
attributeType = AttributeStatementHelper.createAttributeType(mappingModel); attributeType = AttributeStatementHelper.createAttributeType(mappingModel);
roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType)); roleAttributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(attributeType));

View file

@ -48,9 +48,9 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.net.URI; import java.net.URI;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -143,7 +143,7 @@ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationPr
// See if we have existing pairwise mapper and update it. Otherwise create new // See if we have existing pairwise mapper and update it. Otherwise create new
AtomicBoolean foundPairwise = new AtomicBoolean(false); AtomicBoolean foundPairwise = new AtomicBoolean(false);
clientModel.getProtocolMappers().stream().filter((ProtocolMapperModel mapping) -> { clientModel.getProtocolMappersStream().filter((ProtocolMapperModel mapping) -> {
if (mapping.getProtocolMapper().endsWith(AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX)) { if (mapping.getProtocolMapper().endsWith(AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX)) {
foundPairwise.set(true); foundPairwise.set(true);
return true; return true;
@ -163,19 +163,16 @@ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationPr
} else { } else {
// Rather find and remove all pairwise mappers // Rather find and remove all pairwise mappers
clientModel.getProtocolMappers().stream().filter((ProtocolMapperModel mapperRep) -> { clientModel.getProtocolMappersStream()
return mapperRep.getProtocolMapper().endsWith(AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX); .filter(mapperRep -> mapperRep.getProtocolMapper().endsWith(AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX))
}).forEach((ProtocolMapperModel mapping) -> { .collect(Collectors.toList())
clientModel.getProtocolMappers().remove(mapping); .forEach(clientModel::removeProtocolMapper);
});
} }
} }
private void updateClientRepWithProtocolMappers(ClientModel clientModel, ClientRepresentation rep) { private void updateClientRepWithProtocolMappers(ClientModel clientModel, ClientRepresentation rep) {
List<ProtocolMapperRepresentation> mappings = new LinkedList<>(); List<ProtocolMapperRepresentation> mappings =
for (ProtocolMapperModel model : clientModel.getProtocolMappers()) { clientModel.getProtocolMappersStream().map(ModelToRepresentation::toRepresentation).collect(Collectors.toList());
mappings.add(ModelToRepresentation.toRepresentation(model));
}
rep.setProtocolMappers(mappings); rep.setProtocolMappers(mappings);
} }
} }

View file

@ -18,13 +18,12 @@
package org.keycloak.services.clientregistration.policy.impl; package org.keycloak.services.clientregistration.policy.impl;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.stream.Collectors;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.services.ServicesLogger; import org.keycloak.services.ServicesLogger;
import org.keycloak.services.clientregistration.ClientRegistrationContext; import org.keycloak.services.clientregistration.ClientRegistrationContext;
@ -75,19 +74,12 @@ public class ProtocolMappersClientRegistrationPolicy implements ClientRegistrati
public void afterRegister(ClientRegistrationContext context, ClientModel clientModel) { public void afterRegister(ClientRegistrationContext context, ClientModel clientModel) {
// Remove mappers of unsupported type, which were added "automatically" // Remove mappers of unsupported type, which were added "automatically"
List<String> allowedMapperProviders = getAllowedMapperProviders(); List<String> allowedMapperProviders = getAllowedMapperProviders();
Set<ProtocolMapperModel> createdMappers = clientModel.getProtocolMappers(); clientModel.getProtocolMappersStream()
.filter(mapper -> !allowedMapperProviders.contains(mapper.getProtocolMapper()))
createdMappers.stream().filter((ProtocolMapperModel mapper) -> { .peek(mapperToRemove -> logger.debugf("Removing builtin mapper '%s' of type '%s' as type is not permitted",
mapperToRemove.getName(), mapperToRemove.getProtocolMapper()))
return !allowedMapperProviders.contains(mapper.getProtocolMapper()); .collect(Collectors.toList())
.forEach(clientModel::removeProtocolMapper);
}).forEach((ProtocolMapperModel mapperToRemove) -> {
logger.debugf("Removing builtin mapper '%s' of type '%s' as type is not permitted", mapperToRemove.getName(), mapperToRemove.getProtocolMapper());
clientModel.removeProtocolMapper(mapperToRemove);
});
} }
// We don't take already existing protocolMappers into consideration for now // We don't take already existing protocolMappers into consideration for now

View file

@ -91,7 +91,6 @@ import javax.ws.rs.core.UriInfo;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -1098,10 +1097,9 @@ public class AuthenticationManager {
// todo scope param protocol independent // todo scope param protocol independent
String scopeParam = authSession.getClientNote(OAuth2Constants.SCOPE); String scopeParam = authSession.getClientNote(OAuth2Constants.SCOPE);
Set<String> requestedClientScopes = new HashSet<String>(); Set<String> requestedClientScopes = TokenManager.getRequestedClientScopes(scopeParam, client)
for (ClientScopeModel clientScope : org.keycloak.protocol.oidc.TokenManager.getRequestedClientScopes(scopeParam, client)) { .map(ClientScopeModel::getId).collect(Collectors.toSet());
requestedClientScopes.add(clientScope.getId());
}
authSession.setClientScopes(requestedClientScopes); authSession.setClientScopes(requestedClientScopes);
} }

View file

@ -48,6 +48,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.stream.Collectors;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -85,8 +86,7 @@ public class ClientManager {
// remove default mappers if there is a template // remove default mappers if there is a template
if (rep.getProtocolMappers() == null && rep.getClientTemplate() != null) { if (rep.getProtocolMappers() == null && rep.getClientTemplate() != null) {
Set<ProtocolMapperModel> mappers = client.getProtocolMappers(); client.getProtocolMappersStream().collect(Collectors.toList()).forEach(client::removeProtocolMapper);
for (ProtocolMapperModel mapper : mappers) client.removeProtocolMapper(mapper);
} }
return client; return client;

View file

@ -19,9 +19,8 @@ package org.keycloak.services.resources.admin;
import static org.keycloak.protocol.ProtocolMapperUtils.isEnabled; import static org.keycloak.protocol.ProtocolMapperUtils.isEnabled;
import java.util.LinkedList; import java.util.Objects;
import java.util.List; import java.util.stream.Stream;
import java.util.Set;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException; import javax.ws.rs.NotFoundException;
@ -114,43 +113,36 @@ public class ClientScopeEvaluateResource {
@Path("protocol-mappers") @Path("protocol-mappers")
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public List<ProtocolMapperEvaluationRepresentation> getGrantedProtocolMappers(@QueryParam("scope") String scopeParam) { public Stream<ProtocolMapperEvaluationRepresentation> getGrantedProtocolMappers(@QueryParam("scope") String scopeParam) {
auth.clients().requireView(client); auth.clients().requireView(client);
List<ProtocolMapperEvaluationRepresentation> protocolMappers = new LinkedList<>(); return TokenManager.getRequestedClientScopes(scopeParam, client)
.flatMap(mapperContainer -> mapperContainer.getProtocolMappersStream()
Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client); .filter(current -> isEnabled(session, current) && Objects.equals(current.getProtocol(), client.getProtocol()))
.map(current -> toProtocolMapperEvaluationRepresentation(current, mapperContainer)));
for (ClientScopeModel mapperContainer : clientScopes) {
Set<ProtocolMapperModel> currentMappers = mapperContainer.getProtocolMappers();
for (ProtocolMapperModel current : currentMappers) {
if (isEnabled(session, current) && current.getProtocol().equals(client.getProtocol())) {
ProtocolMapperEvaluationRepresentation rep = new ProtocolMapperEvaluationRepresentation();
rep.setMapperId(current.getId());
rep.setMapperName(current.getName());
rep.setProtocolMapper(current.getProtocolMapper());
if (mapperContainer.getId().equals(client.getId())) {
// Must be this client
rep.setContainerId(client.getId());
rep.setContainerName("");
rep.setContainerType("client");
} else {
ClientScopeModel clientScope = (ClientScopeModel) mapperContainer;
rep.setContainerId(clientScope.getId());
rep.setContainerName(clientScope.getName());
rep.setContainerType("client-scope");
}
protocolMappers.add(rep);
}
}
}
return protocolMappers;
} }
private ProtocolMapperEvaluationRepresentation toProtocolMapperEvaluationRepresentation(ProtocolMapperModel mapper,
ClientScopeModel mapperContainer) {
ProtocolMapperEvaluationRepresentation rep = new ProtocolMapperEvaluationRepresentation();
rep.setMapperId(mapper.getId());
rep.setMapperName(mapper.getName());
rep.setProtocolMapper(mapper.getProtocolMapper());
if (mapperContainer.getId().equals(client.getId())) {
// Must be this client
rep.setContainerId(client.getId());
rep.setContainerName("");
rep.setContainerType("client");
} else {
ClientScopeModel clientScope = mapperContainer;
rep.setContainerId(clientScope.getId());
rep.setContainerName(clientScope.getName());
rep.setContainerType("client-scope");
}
return rep;
}
/** /**
* Create JSON with payload of example access token * Create JSON with payload of example access token

View file

@ -17,9 +17,8 @@
package org.keycloak.services.resources.admin; package org.keycloak.services.resources.admin;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.BiPredicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -73,12 +72,8 @@ public class ClientScopeEvaluateScopeMappingsResource {
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@NoCache @NoCache
public List<RoleRepresentation> getGrantedScopeMappings() { public Stream<RoleRepresentation> getGrantedScopeMappings() {
return getGrantedRoles().stream().map((RoleModel role) -> { return getGrantedRoles().map(ModelToRepresentation::toBriefRepresentation);
return ModelToRepresentation.toBriefRepresentation(role);
}).collect(Collectors.toList());
} }
@ -93,31 +88,30 @@ public class ClientScopeEvaluateScopeMappingsResource {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@NoCache @NoCache
public Stream<RoleRepresentation> getNotGrantedScopeMappings() { public Stream<RoleRepresentation> getNotGrantedScopeMappings() {
Set<RoleModel> grantedRoles = getGrantedRoles(); Set<RoleModel> grantedRoles = getGrantedRoles().collect(Collectors.toSet());
return roleContainer.getRolesStream() return roleContainer.getRolesStream()
.filter(r -> !grantedRoles.contains(r)) .filter(((Predicate<RoleModel>) grantedRoles::contains).negate())
.map(ModelToRepresentation::toBriefRepresentation); .map(ModelToRepresentation::toBriefRepresentation);
} }
private Set<RoleModel> getGrantedRoles() { private Stream<RoleModel> getGrantedRoles() {
if (client.isFullScopeAllowed()) { if (client.isFullScopeAllowed()) {
// intentionally using deprecated method as a set is more appropriate here return roleContainer.getRolesStream();
return roleContainer.getRoles();
} }
Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client); Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client)
.collect(Collectors.toSet());
BiPredicate<Set<ClientScopeModel>, RoleModel> hasClientScope = (scopes, role) -> Predicate<RoleModel> hasClientScope = role ->
scopes.stream().anyMatch(scopeContainer -> scopeContainer.hasScope(role)); clientScopes.stream().anyMatch(scopeContainer -> scopeContainer.hasScope(role));
return roleContainer.getRolesStream() return roleContainer.getRolesStream()
.filter(auth.roles()::canView) .filter(auth.roles()::canView)
.filter(r -> hasClientScope.test(clientScopes, r)) .filter(hasClientScope);
.collect(Collectors.toSet());
} }
} }

View file

@ -49,9 +49,10 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Properties; import java.util.Properties;
import java.util.stream.Stream;
/** /**
* Base resource for managing users * Base resource for managing users
@ -99,14 +100,12 @@ public class ProtocolMappersResource {
@NoCache @NoCache
@Path("protocol/{protocol}") @Path("protocol/{protocol}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public List<ProtocolMapperRepresentation> getMappersPerProtocol(@PathParam("protocol") String protocol) { public Stream<ProtocolMapperRepresentation> getMappersPerProtocol(@PathParam("protocol") String protocol) {
viewPermission.require(); viewPermission.require();
List<ProtocolMapperRepresentation> mappers = new LinkedList<ProtocolMapperRepresentation>(); return client.getProtocolMappersStream()
for (ProtocolMapperModel mapper : client.getProtocolMappers()) { .filter(mapper -> isEnabled(session, mapper) && Objects.equals(mapper.getProtocol(), protocol))
if (isEnabled(session, mapper) && mapper.getProtocol().equals(protocol)) mappers.add(ModelToRepresentation.toRepresentation(mapper)); .map(ModelToRepresentation::toRepresentation);
}
return mappers;
} }
/** /**
@ -163,16 +162,12 @@ public class ProtocolMappersResource {
@NoCache @NoCache
@Path("models") @Path("models")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public List<ProtocolMapperRepresentation> getMappers() { public Stream<ProtocolMapperRepresentation> getMappers() {
viewPermission.require(); viewPermission.require();
List<ProtocolMapperRepresentation> mappers = new LinkedList<ProtocolMapperRepresentation>(); return client.getProtocolMappersStream()
for (ProtocolMapperModel mapper : client.getProtocolMappers()) { .filter(mapper -> isEnabled(session, mapper))
if (isEnabled(session, mapper)) { .map(ModelToRepresentation::toRepresentation);
mappers.add(ModelToRepresentation.toRepresentation(mapper));
}
}
return mappers;
} }
/** /**

View file

@ -20,7 +20,9 @@ package org.keycloak.services.util;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects;
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; import java.util.stream.Stream;
@ -81,7 +83,7 @@ public class DefaultClientSessionContext implements ClientSessionContext {
public static DefaultClientSessionContext fromClientSessionAndScopeParameter(AuthenticatedClientSessionModel clientSession, String scopeParam, KeycloakSession session) { public static DefaultClientSessionContext fromClientSessionAndScopeParameter(AuthenticatedClientSessionModel clientSession, String scopeParam, KeycloakSession session) {
Set<ClientScopeModel> requestedClientScopes = TokenManager.getRequestedClientScopes(scopeParam, clientSession.getClient()); Stream<ClientScopeModel> requestedClientScopes = TokenManager.getRequestedClientScopes(scopeParam, clientSession.getClient());
return fromClientSessionAndClientScopes(clientSession, requestedClientScopes, session); return fromClientSessionAndClientScopes(clientSession, requestedClientScopes, session);
} }
@ -91,12 +93,10 @@ public class DefaultClientSessionContext implements ClientSessionContext {
} }
public static DefaultClientSessionContext fromClientSessionAndClientScopes(AuthenticatedClientSessionModel clientSession, Set<ClientScopeModel> clientScopes, KeycloakSession session) { public static DefaultClientSessionContext fromClientSessionAndClientScopes(AuthenticatedClientSessionModel clientSession,
Set<String> clientScopeIds = new HashSet<>(); Stream<ClientScopeModel> clientScopes,
for (ClientScopeModel clientScope : clientScopes) { KeycloakSession session) {
clientScopeIds.add(clientScope.getId()); Set<String> clientScopeIds = clientScopes.map(ClientScopeModel::getId).collect(Collectors.toSet());
}
return new DefaultClientSessionContext(clientSession, clientScopeIds, session); return new DefaultClientSessionContext(clientSession, clientScopeIds, session);
} }
@ -114,12 +114,12 @@ public class DefaultClientSessionContext implements ClientSessionContext {
@Override @Override
public Set<ClientScopeModel> getClientScopes() { public Stream<ClientScopeModel> getClientScopesStream() {
// Load client scopes if not yet present // Load client scopes if not yet present
if (clientScopes == null) { if (clientScopes == null) {
clientScopes = loadClientScopes(); clientScopes = loadClientScopes();
} }
return clientScopes; return clientScopes.stream();
} }
@ -134,12 +134,12 @@ public class DefaultClientSessionContext implements ClientSessionContext {
@Override @Override
public Set<ProtocolMapperModel> getProtocolMappers() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
// Load protocolMappers if not yet present // Load protocolMappers if not yet present
if (protocolMappers == null) { if (protocolMappers == null) {
protocolMappers = loadProtocolMappers(); protocolMappers = loadProtocolMappers();
} }
return protocolMappers; return protocolMappers.stream();
} }
@ -154,28 +154,12 @@ public class DefaultClientSessionContext implements ClientSessionContext {
@Override @Override
public String getScopeString() { public String getScopeString() {
StringBuilder builder = new StringBuilder();
// Add both default and optional scopes to scope parameter. Don't add client itself // Add both default and optional scopes to scope parameter. Don't add client itself
boolean first = true; String scopeParam = getClientScopesStream()
for (ClientScopeModel clientScope : getClientScopes()) { .filter(((Predicate<ClientScopeModel>) ClientModel.class::isInstance).negate())
if (clientScope instanceof ClientModel) { .filter(ClientScopeModel::isIncludeInTokenScope)
continue; .map(ClientScopeModel::getName)
} .collect(Collectors.joining(" "));
if (!clientScope.isIncludeInTokenScope()) {
continue;
}
if (first) {
first = false;
} else {
builder.append(" ");
}
builder.append(clientScope.getName());
}
String scopeParam = builder.toString();
// See if "openid" scope is requested // See if "openid" scope is requested
String scopeSent = clientSession.getNote(OAuth2Constants.SCOPE); String scopeSent = clientSession.getNote(OAuth2Constants.SCOPE);
@ -246,34 +230,26 @@ public class DefaultClientSessionContext implements ClientSessionContext {
private Set<RoleModel> loadRoles() { private Set<RoleModel> loadRoles() {
UserModel user = clientSession.getUserSession().getUser(); UserModel user = clientSession.getUserSession().getUser();
ClientModel client = clientSession.getClient(); ClientModel client = clientSession.getClient();
return TokenManager.getAccess(user, client, getClientScopesStream());
Set<ClientScopeModel> clientScopes = getClientScopes();
return TokenManager.getAccess(user, client, clientScopes);
} }
private Set<ProtocolMapperModel> loadProtocolMappers() { private Set<ProtocolMapperModel> loadProtocolMappers() {
Set<ClientScopeModel> clientScopes = getClientScopes();
String protocol = clientSession.getClient().getProtocol(); String protocol = clientSession.getClient().getProtocol();
// Being rather defensive. But protocol should normally always be there // Being rather defensive. But protocol should normally always be there
if (protocol == null) { if (protocol == null) {
logger.warnf("Client '%s' doesn't have protocol set. Fallback to openid-connect. Please fix client configuration", clientSession.getClient().getClientId()); logger.warnf("Client '%s' doesn't have protocol set. Fallback to openid-connect. Please fix client configuration",
clientSession.getClient().getClientId());
protocol = OIDCLoginProtocol.LOGIN_PROTOCOL; protocol = OIDCLoginProtocol.LOGIN_PROTOCOL;
} }
Set<ProtocolMapperModel> protocolMappers = new HashSet<>(); String finalProtocol = protocol;
for (ClientScopeModel clientScope : clientScopes) { return getClientScopesStream()
Set<ProtocolMapperModel> currentMappers = clientScope.getProtocolMappers(); .flatMap(clientScope -> clientScope.getProtocolMappersStream()
for (ProtocolMapperModel currentMapper : currentMappers) { .filter(mapper -> Objects.equals(finalProtocol, mapper.getProtocol()) &&
if (protocol.equals(currentMapper.getProtocol()) && ProtocolMapperUtils.isEnabled(session, currentMapper)) { ProtocolMapperUtils.isEnabled(session, mapper)))
protocolMappers.add(currentMapper); .collect(Collectors.toSet());
}
}
}
return protocolMappers;
} }

View file

@ -62,13 +62,12 @@ public final class OpenshiftSAClientAdapter extends AbstractReadOnlyClientStorag
private static final String ANNOTATION_OAUTH_REDIRECT_REFERENCE = "serviceaccounts.openshift.io/oauth-redirectreference"; private static final String ANNOTATION_OAUTH_REDIRECT_REFERENCE = "serviceaccounts.openshift.io/oauth-redirectreference";
private static final Pattern ROLE_SCOPE_PATTERN = Pattern.compile("role:([^:]+):([^:!]+)(:[!])?"); private static final Pattern ROLE_SCOPE_PATTERN = Pattern.compile("role:([^:]+):([^:!]+)(:[!])?");
private static final Set<String> OPTIONAL_SCOPES = Stream.of("user:info", "user:check-access").collect(Collectors.toSet()); private static final Set<String> OPTIONAL_SCOPES = Stream.of("user:info", "user:check-access").collect(Collectors.toSet());
private static final Set<ProtocolMapperModel> DEFAULT_PROTOCOL_MAPPERS = createDefaultProtocolMappers();
private static Set<ProtocolMapperModel> createDefaultProtocolMappers() { private static Set<ProtocolMapperModel> createDefaultProtocolMappers() {
Set<ProtocolMapperModel> mappers = new HashSet<>(); Set<ProtocolMapperModel> mappers = new HashSet<>();
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper("username", "username", "preferred_username", "string", true, true, UserPropertyMapper.PROVIDER_ID); ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper("username", "username",
"preferred_username", "string", true, true, UserPropertyMapper.PROVIDER_ID);
mapper.setId(KeycloakModelUtils.generateId()); mapper.setId(KeycloakModelUtils.generateId());
mappers.add(mapper); mappers.add(mapper);
@ -315,34 +314,33 @@ public final class OpenshiftSAClientAdapter extends AbstractReadOnlyClientStorag
} }
@Override @Override
public Set<ProtocolMapperModel> getProtocolMappers() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
return getConfigOrDefault(() -> {
List<ProtocolMapperRepresentation> mappers = defaultConfig.getProtocolMappers(); List<ProtocolMapperRepresentation> mappers = defaultConfig.getProtocolMappers();
if (mappers == null) { if (mappers == null) {
return null; Set<ProtocolMapperModel> defaultProtocolMappers = createDefaultProtocolMappers();
defaultConfig.setProtocolMappers(defaultProtocolMappers.stream()
.map(ModelToRepresentation::toRepresentation).collect(Collectors.toList()));
return defaultProtocolMappers.stream();
} }
Set<ProtocolMapperModel> model = new HashSet<>(); return mappers.stream().map(RepresentationToModel::toModel);
for (ProtocolMapperRepresentation mapper : mappers) {
model.add(RepresentationToModel.toModel(mapper));
}
return model;
}, (Consumer<Set<ProtocolMapperModel>>) mappers -> {
defaultConfig.setProtocolMappers(mappers.stream().map(ModelToRepresentation::toRepresentation).collect(Collectors.toList()));
}, (Supplier<Set<ProtocolMapperModel>>) () -> DEFAULT_PROTOCOL_MAPPERS);
} }
@Override @Override
public ProtocolMapperModel getProtocolMapperById(String id) { public ProtocolMapperModel getProtocolMapperById(String id) {
return getProtocolMappers().stream().filter(protocolMapperModel -> id.equals(protocolMapperModel.getId())).findAny().get(); return getProtocolMappersStream()
.filter(protocolMapperModel -> Objects.equals(id, protocolMapperModel.getId()))
.findAny()
.orElse(null);
} }
@Override @Override
public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) { public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) {
return getProtocolMappers().stream().filter(protocolMapperModel -> name.equals(protocolMapperModel.getName())).findAny().get(); return getProtocolMappersStream()
.filter(protocolMapperModel -> Objects.equals(name, protocolMapperModel.getName()))
.findAny()
.orElse(null);
} }
@Override @Override
@ -452,8 +450,8 @@ public final class OpenshiftSAClientAdapter extends AbstractReadOnlyClientStorag
} }
@Override @Override
public Set<ProtocolMapperModel> getProtocolMappers() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
return DEFAULT_PROTOCOL_MAPPERS; return createDefaultProtocolMappers().stream();
} }
@Override @Override

View file

@ -261,8 +261,8 @@ public class HardcodedClientStorageProvider implements ClientStorageProvider, Cl
} }
@Override @Override
public Set<ProtocolMapperModel> getProtocolMappers() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
return Collections.EMPTY_SET; return Stream.empty();
} }
@Override @Override

View file

@ -32,7 +32,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
@ -52,6 +51,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED; import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
@ -108,14 +108,14 @@ public class UserStorageConsentTest extends AbstractServletsAdapterTest {
product.setConsentRequired(true); product.setConsentRequired(true);
ClientScopeModel clientScope = realm.addClientScope("clientScope"); ClientScopeModel clientScope = realm.addClientScope("clientScope");
clientScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); clientScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
System.err.println("client scope protocol mappers size: " + clientScope.getProtocolMappers().size()); System.err.println("client scope protocol mappers size: " + clientScope.getProtocolMappersStream().count());
for (ProtocolMapperModel mapper : product.getProtocolMappers()) { for (ProtocolMapperModel mapper : product.getProtocolMappersStream().collect(Collectors.toList())) {
if (mapper.getProtocol().equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) { if (mapper.getProtocol().equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
if (mapper.getName().equals(OIDCLoginProtocolFactory.USERNAME) if (mapper.getName().equals(OIDCLoginProtocolFactory.USERNAME)
|| mapper.getName().equals(OIDCLoginProtocolFactory.EMAIL) || mapper.getName().equals(OIDCLoginProtocolFactory.EMAIL)
|| mapper.getName().equals(OIDCLoginProtocolFactory.GIVEN_NAME) || mapper.getName().equals(OIDCLoginProtocolFactory.GIVEN_NAME)
) { ) {
ProtocolMapperModel copy = new ProtocolMapperModel(); ProtocolMapperModel copy = new ProtocolMapperModel();
copy.setName(mapper.getName()); copy.setName(mapper.getName());
copy.setProtocol(mapper.getProtocol()); copy.setProtocol(mapper.getProtocol());