KEYCLOAK-17460 invalidate client when assigning scope

This commit is contained in:
vramik 2021-03-16 15:24:25 +01:00 committed by Hynek Mlnařík
parent e10f3b3672
commit c3b9c66941
39 changed files with 381 additions and 186 deletions

View file

@ -27,8 +27,8 @@ import org.keycloak.models.cache.infinispan.entities.CachedClient;
import org.keycloak.models.utils.RoleUtils;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@ -99,41 +99,22 @@ public class ClientAdapter implements ClientModel, CachedObject {
@Override
public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) {
getDelegateForUpdate();
updated.addClientScope(clientScope, defaultScope);
cacheSession.addClientScopes(getRealm(), this, Collections.singleton(clientScope), defaultScope);
}
@Override
public void addClientScopes(Set<ClientScopeModel> clientScopes, boolean defaultScope) {
for (ClientScopeModel clientScope : clientScopes) {
addClientScope(clientScope, defaultScope);
}
cacheSession.addClientScopes(getRealm(), this, clientScopes, defaultScope);
}
@Override
public void removeClientScope(ClientScopeModel clientScope) {
getDelegateForUpdate();
updated.removeClientScope(clientScope);
cacheSession.removeClientScope(getRealm(), this, clientScope);
}
@Override
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol) {
if (isUpdated()) return updated.getClientScopes(defaultScope, filterByProtocol);
List<String> clientScopeIds = defaultScope ? cached.getDefaultClientScopesIds() : cached.getOptionalClientScopesIds();
// Defaults to openid-connect
String clientProtocol = getProtocol() == null ? "openid-connect" : getProtocol();
Map<String, ClientScopeModel> clientScopes = new HashMap<>();
for (String scopeId : clientScopeIds) {
ClientScopeModel clientScope = cacheSession.getClientScopeById(cachedRealm, scopeId);
if (clientScope != null) {
if (!filterByProtocol || clientScope.getProtocol().equals(clientProtocol)) {
clientScopes.put(clientScope.getName(), clientScope);
}
}
}
return clientScopes;
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope) {
return cacheSession.getClientScopes(getRealm(), this, defaultScope);
}
public void addWebOrigin(String webOrigin) {

View file

@ -232,4 +232,8 @@ public class ClientScopeAdapter implements ClientScopeModel {
return getId().hashCode();
}
@Override
public String toString() {
return String.format("%s@%08x", getId(), hashCode());
}
}

View file

@ -97,6 +97,8 @@ public class RealmCacheManager extends CacheManager {
public void clientUpdated(String realmId, String clientUuid, String clientId, Set<String> invalidations) {
invalidations.add(RealmCacheSession.getClientByClientIdCacheKey(clientId, realmId));
invalidations.add(RealmCacheSession.getClientScopesCacheKey(clientUuid, true));
invalidations.add(RealmCacheSession.getClientScopesCacheKey(clientUuid, false));
}
// Client roles invalidated separately

View file

@ -97,6 +97,8 @@ public class RealmCacheSession implements CacheRealmProvider {
protected static final Logger logger = Logger.getLogger(RealmCacheSession.class);
public static final String REALM_CLIENTS_QUERY_SUFFIX = ".realm.clients";
public static final String ROLES_QUERY_SUFFIX = ".roles";
private static final String SCOPE_KEY_DEFAULT = "default";
private static final String SCOPE_KEY_OPTIONAL = "optional";
protected RealmCacheManager cache;
protected KeycloakSession session;
protected RealmProvider realmDelegate;
@ -542,6 +544,10 @@ public class RealmCacheSession implements CacheRealmProvider {
return realm + ".clientscopes";
}
static String getClientScopesCacheKey(String client, boolean defaultScope) {
return client + "." + (defaultScope ? SCOPE_KEY_DEFAULT : SCOPE_KEY_OPTIONAL) + ".clientscopes";
}
static String getTopGroupsQueryCacheKey(String realm) {
return realm + ".top.groups";
}
@ -1305,6 +1311,51 @@ public class RealmCacheSession implements CacheRealmProvider {
realm.getClientScopesStream().map(ClientScopeModel::getId).forEach(id -> removeClientScope(realm, id));
}
@Override
public void addClientScopes(RealmModel realm, ClientModel client, Set<ClientScopeModel> clientScopes, boolean defaultScope) {
getClientDelegate().addClientScopes(realm, client, clientScopes, defaultScope);
registerClientInvalidation(client.getId(), client.getId(), realm.getId());
}
@Override
public void removeClientScope(RealmModel realm, ClientModel client, ClientScopeModel clientScope) {
getClientDelegate().removeClientScope(realm, client, clientScope);
registerClientInvalidation(client.getId(), client.getId(), realm.getId());
}
@Override
public Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScopes) {
String cacheKey = getClientScopesCacheKey(client.getId(), defaultScopes);
boolean queryDB = invalidations.contains(cacheKey) || invalidations.contains(client.getId()) || listInvalidations.contains(realm.getId());
if (queryDB) {
return getClientDelegate().getClientScopes(realm, client, defaultScopes);
}
ClientScopeListQuery query = cache.get(cacheKey, ClientScopeListQuery.class);
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
Map<String, ClientScopeModel> model = getClientDelegate().getClientScopes(realm, client, defaultScopes);
if (model == null) return null;
Set<String> ids = model.values().stream().map(ClientScopeModel::getId).collect(Collectors.toSet());
query = new ClientScopeListQuery(loaded, cacheKey, realm, client.getId(), ids);
logger.tracev("adding assigned client scopes cache miss: client {0} key {1}", client.getClientId(), cacheKey);
cache.addRevisioned(query, startupRevision);
return model;
}
Map<String, ClientScopeModel> assignedScopes = new HashMap<>();
for (String id : query.getClientScopes()) {
ClientScopeModel clientScope = session.clientScopes().getClientScopeById(realm, id);
if (clientScope == null) {
invalidations.add(cacheKey);
return getClientDelegate().getClientScopes(realm, client, defaultScopes);
}
if (clientScope.getProtocol().equals((client.getProtocol() == null) ? "openid-connect" : client.getProtocol())) {
assignedScopes.put(clientScope.getName(), clientScope);
}
}
return assignedScopes;
}
// Don't cache ClientInitialAccessModel for now
@Override
public ClientInitialAccessModel createClientInitialAccessModel(RealmModel realm, int expiration, int count) {

View file

@ -109,11 +109,11 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
registeredNodes = new TreeMap<>(model.getRegisteredNodes());
defaultClientScopesIds = new LinkedList<>();
for (ClientScopeModel clientScope : model.getClientScopes(true, false).values()) {
for (ClientScopeModel clientScope : model.getClientScopes(true).values()) {
defaultClientScopesIds.add(clientScope.getId());
}
optionalClientScopesIds = new LinkedList<>();
for (ClientScopeModel clientScope : model.getClientScopes(false, false).values()) {
for (ClientScopeModel clientScope : model.getClientScopes(false).values()) {
optionalClientScopesIds.add(clientScope.getId());
}
}

View file

@ -25,6 +25,7 @@ public class ClientScopeListQuery extends AbstractRevisioned implements ClientSc
private final Set<String> clientScopes;
private final String realm;
private final String realmName;
private String clientUuid;
public ClientScopeListQuery(Long revisioned, String id, RealmModel realm, Set<String> clientScopes) {
super(revisioned, id);
@ -33,6 +34,11 @@ public class ClientScopeListQuery extends AbstractRevisioned implements ClientSc
this.clientScopes = clientScopes;
}
public ClientScopeListQuery(Long revisioned, String id, RealmModel realm, String clientUuid, Set<String> clientScopes) {
this(revisioned, id, realm, clientScopes);
this.clientUuid = clientUuid;
}
@Override
public Set<String> getClientScopes() {
return clientScopes;
@ -43,11 +49,17 @@ public class ClientScopeListQuery extends AbstractRevisioned implements ClientSc
return realm;
}
@Override
public String getClientId() {
return clientUuid;
}
@Override
public String toString() {
return "ClientScopeListQuery{" +
"id='" + getId() + "'" +
", realmName='" + realmName + '\'' +
", clientUuid='" + clientUuid + '\'' +
'}';
}
}

View file

@ -19,6 +19,6 @@ package org.keycloak.models.cache.infinispan.entities;
import java.util.Set;
public interface ClientScopeQuery extends InRealm {
public interface ClientScopeQuery extends InClient {
Set<String> getClientScopes();
}

View file

@ -26,20 +26,17 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.jpa.entities.ClientAttributeEntity;
import org.keycloak.models.jpa.entities.ClientEntity;
import org.keycloak.models.jpa.entities.ClientScopeClientMappingEntity;
import org.keycloak.models.jpa.entities.ProtocolMapperEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@ -265,7 +262,7 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
@Override
public void setProtocol(String protocol) {
entity.setProtocol(protocol);
session.getKeycloakSessionFactory().publish((ClientModel.ClientProtocolUpdatedEvent) () -> ClientAdapter.this);
}
@Override
@ -349,57 +346,22 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
@Override
public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) {
if (getClientScopes(defaultScope, false).containsKey(clientScope.getName())) return;
persist(clientScope, defaultScope);
addClientScopes(Collections.singleton(clientScope), defaultScope);
}
@Override
public void addClientScopes(Set<ClientScopeModel> clientScopes, boolean defaultScope) {
Map<String, ClientScopeModel> existingClientScopes = getClientScopes(defaultScope, false);
clientScopes.stream()
.filter(clientScope -> !existingClientScopes.containsKey(clientScope.getName()))
.forEach(clientScope -> persist(clientScope, defaultScope));
}
private void persist(ClientScopeModel clientScope, boolean defaultScope) {
ClientScopeClientMappingEntity entity = new ClientScopeClientMappingEntity();
entity.setClientScopeId(clientScope.getId());
entity.setClient(getEntity());
entity.setDefaultScope(defaultScope);
em.persist(entity);
em.flush();
em.detach(entity);
session.clients().addClientScopes(getRealm(), this, clientScopes, defaultScope);
}
@Override
public void removeClientScope(ClientScopeModel clientScope) {
int numRemoved = em.createNamedQuery("deleteClientScopeClientMapping")
.setParameter("clientScopeId", clientScope.getId())
.setParameter("client", getEntity())
.executeUpdate();
em.flush();
session.clients().removeClientScope(getRealm(), this, clientScope);
}
@Override
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol) {
TypedQuery<String> query = em.createNamedQuery("clientScopeClientMappingIdsByClient", String.class);
query.setParameter("client", getEntity());
query.setParameter("defaultScope", defaultScope);
List<String> ids = query.getResultList();
// Defaults to openid-connect
String clientProtocol = getProtocol() == null ? OIDCLoginProtocol.LOGIN_PROTOCOL : getProtocol();
Map<String, ClientScopeModel> clientScopes = new HashMap<>();
for (String clientScopeId : ids) {
ClientScopeModel clientScope = realm.getClientScopeById(clientScopeId);
if (clientScope == null) continue;
if (!filterByProtocol || clientScope.getProtocol().equals(clientProtocol)) {
clientScopes.put(clientScope.getName(), clientScope);
}
}
return clientScopes;
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope) {
return session.clients().getClientScopes(getRealm(), this, defaultScope);
}

View file

@ -300,7 +300,9 @@ public class ClientScopeAdapter implements ClientScopeModel, JpaModel<ClientScop
return getId().hashCode();
}
@Override
public String toString() {
return String.format("%s@%08x", getId(), hashCode());
}
}

View file

@ -25,6 +25,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
@ -54,11 +56,13 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.RoleProvider;
import org.keycloak.models.jpa.entities.ClientEntity;
import org.keycloak.models.jpa.entities.ClientInitialAccessEntity;
import org.keycloak.models.jpa.entities.ClientScopeClientMappingEntity;
import org.keycloak.models.jpa.entities.ClientScopeEntity;
import org.keycloak.models.jpa.entities.GroupEntity;
import org.keycloak.models.jpa.entities.RealmEntity;
import org.keycloak.models.jpa.entities.RealmLocalizationTextsEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.models.utils.KeycloakModelUtils;
/**
@ -715,7 +719,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
});
int countRemoved = em.createNamedQuery("deleteClientScopeClientMappingByClient")
.setParameter("client", clientEntity)
.setParameter("clientId", clientEntity.getId())
.executeUpdate();
em.remove(clientEntity); // i have no idea why, but this needs to come before deleteScopeMapping
@ -790,6 +794,52 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
realm.getClientScopesStream().map(ClientScopeModel::getId).forEach(id -> this.removeClientScope(realm, id));
}
@Override
public void addClientScopes(RealmModel realm, ClientModel client, Set<ClientScopeModel> clientScopes, boolean defaultScope) {
// Defaults to openid-connect
String clientProtocol = client.getProtocol() == null ? OIDCLoginProtocol.LOGIN_PROTOCOL : client.getProtocol();
Map<String, ClientScopeModel> existingClientScopes = getClientScopes(realm, client, defaultScope);
clientScopes.stream()
.filter(clientScope -> ! existingClientScopes.containsKey(clientScope.getName()))
.filter(clientScope -> Objects.equals(clientScope.getProtocol(), clientProtocol))
.forEach(clientScope -> {
ClientScopeClientMappingEntity entity = new ClientScopeClientMappingEntity();
entity.setClientScopeId(clientScope.getId());
entity.setClientId(client.getId());
entity.setDefaultScope(defaultScope);
em.persist(entity);
em.flush();
em.detach(entity);
});
}
@Override
public void removeClientScope(RealmModel realm, ClientModel client, ClientScopeModel clientScope) {
em.createNamedQuery("deleteClientScopeClientMapping")
.setParameter("clientScopeId", clientScope.getId())
.setParameter("clientId", client.getId())
.executeUpdate();
em.flush();
}
@Override
public Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScope) {
// Defaults to openid-connect
String clientProtocol = client.getProtocol() == null ? OIDCLoginProtocol.LOGIN_PROTOCOL : client.getProtocol();
TypedQuery<String> query = em.createNamedQuery("clientScopeClientMappingIdsByClient", String.class);
query.setParameter("clientId", client.getId());
query.setParameter("defaultScope", defaultScope);
return query.getResultStream()
.map(clientScopeId -> session.clientScopes().getClientScopeById(realm, clientScopeId))
.filter(Objects::nonNull)
.filter(clientScope -> Objects.equals(clientScope.getProtocol(), clientProtocol))
.collect(Collectors.toMap(ClientScopeModel::getName, Function.identity()));
}
@Override
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer first, Integer max) {
TypedQuery<String> query = em.createNamedQuery("getGroupIdsByNameContaining", String.class)

View file

@ -21,11 +21,8 @@ import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@ -36,9 +33,9 @@ import javax.persistence.Table;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@NamedQueries({
@NamedQuery(name="clientScopeClientMappingIdsByClient", query="select m.clientScopeId from ClientScopeClientMappingEntity m where m.client = :client and m.defaultScope = :defaultScope"),
@NamedQuery(name="deleteClientScopeClientMapping", query="delete from ClientScopeClientMappingEntity where client = :client and clientScopeId = :clientScopeId"),
@NamedQuery(name="deleteClientScopeClientMappingByClient", query="delete from ClientScopeClientMappingEntity where client = :client")
@NamedQuery(name="clientScopeClientMappingIdsByClient", query="select m.clientScopeId from ClientScopeClientMappingEntity m where m.clientId = :clientId and m.defaultScope = :defaultScope"),
@NamedQuery(name="deleteClientScopeClientMapping", query="delete from ClientScopeClientMappingEntity where clientId = :clientId and clientScopeId = :clientScopeId"),
@NamedQuery(name="deleteClientScopeClientMappingByClient", query="delete from ClientScopeClientMappingEntity where clientId = :clientId")
})
@Entity
@Table(name="CLIENT_SCOPE_CLIENT")
@ -50,9 +47,8 @@ public class ClientScopeClientMappingEntity {
protected String clientScopeId;
@Id
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name="CLIENT_ID")
protected ClientEntity client;
@Column(name="CLIENT_ID")
protected String clientId;
@Column(name="DEFAULT_SCOPE")
protected boolean defaultScope;
@ -65,12 +61,12 @@ public class ClientScopeClientMappingEntity {
this.clientScopeId = clientScopeId;
}
public ClientEntity getClient() {
return client;
public String getClientId() {
return clientId;
}
public void setClient(ClientEntity client) {
this.client = client;
public void setClientId(String clientId) {
this.clientId = clientId;
}
public boolean isDefaultScope() {
@ -85,22 +81,22 @@ public class ClientScopeClientMappingEntity {
protected String clientScopeId;
protected ClientEntity client;
protected String clientId;
public Key() {
}
public Key(String clientScopeId, ClientEntity client) {
public Key(String clientScopeId, String clientId) {
this.clientScopeId = clientScopeId;
this.client = client;
this.clientId = clientId;
}
public String getClientScopeId() {
return clientScopeId;
}
public ClientEntity getClient() {
return client;
public String getClientId() {
return clientId;
}
@Override
@ -111,7 +107,7 @@ public class ClientScopeClientMappingEntity {
ClientScopeClientMappingEntity.Key key = (ClientScopeClientMappingEntity.Key) o;
if (clientScopeId != null ? !clientScopeId.equals(key.getClientScopeId() != null ? key.getClientScopeId() : null) : key.getClientScopeId() != null) return false;
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
if (clientId != null ? !clientId.equals(key.getClientId() != null ? key.getClientId() : null) : key.getClientId() != null) return false;
return true;
}
@ -119,7 +115,7 @@ public class ClientScopeClientMappingEntity {
@Override
public int hashCode() {
int result = clientScopeId != null ? clientScopeId.hashCode() : 0;
result = 31 * result + (client != null ? client.getId().hashCode() : 0);
result = 31 * result + (clientId != null ? clientId.hashCode() : 0);
return result;
}
}
@ -133,7 +129,7 @@ public class ClientScopeClientMappingEntity {
ClientScopeClientMappingEntity key = (ClientScopeClientMappingEntity) o;
if (clientScopeId != null ? !clientScopeId.equals(key.getClientScopeId() != null ? key.getClientScopeId() : null) : key.getClientScopeId() != null) return false;
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
if (clientId != null ? !clientId.equals(key.getClientId() != null ? key.getClientId() : null) : key.getClientId() != null) return false;
return true;
}
@ -141,7 +137,7 @@ public class ClientScopeClientMappingEntity {
@Override
public int hashCode() {
int result = clientScopeId != null ? clientScopeId.hashCode() : 0;
result = 31 * result + (client != null ? client.getId().hashCode() : 0);
result = 31 * result + (clientId != null ? clientId.hashCode() : 0);
return result;
}
}

View file

@ -41,5 +41,11 @@
<changeSet author="keycloak" id="map-remove-ri-13.0.0">
<dropForeignKeyConstraint baseTableName="DEFAULT_CLIENT_SCOPE" constraintName="FK_R_DEF_CLI_SCOPE_SCOPE"/>
<dropForeignKeyConstraint baseTableName="CLIENT_SCOPE_CLIENT" constraintName="FK_C_CLI_SCOPE_SCOPE"/>
<dropForeignKeyConstraint baseTableName="CLIENT_SCOPE_CLIENT" constraintName="FK_C_CLI_SCOPE_CLIENT"/>
</changeSet>
<changeSet author="keycloak" id="13.0.0-increase-column-size-federated">
<modifyDataType newDataType="VARCHAR(255)" tableName="CLIENT_SCOPE_CLIENT" columnName="CLIENT_ID"/>
<modifyDataType newDataType="VARCHAR(255)" tableName="CLIENT_SCOPE_CLIENT" columnName="SCOPE_ID"/>
</changeSet>
</databaseChangeLog>

View file

@ -16,6 +16,8 @@
*/
package org.keycloak.models.map.client;
import java.util.Collections;
import java.util.Map;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
@ -49,9 +51,22 @@ public abstract class AbstractClientModel<E extends AbstractEntity> implements C
@Override
public void addClientScopes(Set<ClientScopeModel> clientScopes, boolean defaultScope) {
for (ClientScopeModel cs : clientScopes) {
addClientScope(cs, defaultScope);
}
session.clients().addClientScopes(getRealm(), this, clientScopes, defaultScope);
}
@Override
public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) {
addClientScopes(Collections.singleton(clientScope), defaultScope);
}
@Override
public void removeClientScope(ClientScopeModel clientScope) {
session.clients().removeClientScope(getRealm(), this, clientScope);
}
@Override
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope) {
return session.clients().getClientScopes(getRealm(), this, defaultScope);
}
@Override

View file

@ -17,20 +17,16 @@
package org.keycloak.models.map.client;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import com.google.common.base.Functions;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@ -241,6 +237,7 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override
public void setProtocol(String protocol) {
entity.setProtocol(protocol);
session.getKeycloakSessionFactory().publish((ClientModel.ClientProtocolUpdatedEvent) () -> MapClientAdapter.this);
}
@Override
@ -385,38 +382,6 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
entity.setNotBefore(notBefore);
}
/*************** Client scopes ****************/
@Override
public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) {
final String id = clientScope == null ? null : clientScope.getId();
if (id != null) {
entity.addClientScope(id, defaultScope);
}
}
@Override
public void removeClientScope(ClientScopeModel clientScope) {
final String id = clientScope == null ? null : clientScope.getId();
if (id != null) {
entity.removeClientScope(id);
}
}
@Override
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol) {
Stream<ClientScopeModel> res = this.entity.getClientScopes(defaultScope)
.map(realm::getClientScopeById)
.filter(Objects::nonNull);
if (filterByProtocol) {
String clientProtocol = getProtocol() == null ? OIDCLoginProtocol.LOGIN_PROTOCOL : getProtocol();
res = res.filter(cs -> Objects.equals(cs.getProtocol(), clientProtocol));
}
return res.collect(Collectors.toMap(ClientScopeModel::getName, Functions.identity()));
}
/*************** Scopes mappings ****************/
@Override

View file

@ -29,8 +29,10 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.common.Serialization;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@ -43,6 +45,8 @@ import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import static org.keycloak.utils.StreamsUtil.paginatedStream;
public class MapClientProvider implements ClientProvider {
@ -269,6 +273,54 @@ public class MapClientProvider implements ClientProvider {
return paginatedStream(s, firstResult, maxResults).map(entityToAdapterFunc(realm));
}
@Override
public void addClientScopes(RealmModel realm, ClientModel client, Set<ClientScopeModel> clientScopes, boolean defaultScope) {
MapClientEntity entity = tx.read(UUID.fromString(client.getId()));
if (entity == null) return;
// Defaults to openid-connect
String clientProtocol = client.getProtocol() == null ? OIDCLoginProtocol.LOGIN_PROTOCOL : client.getProtocol();
LOG.tracef("addClientScopes(%s, %s, %s, %b)%s", realm, client, clientScopes, defaultScope, getShortStackTrace());
Map<String, ClientScopeModel> existingClientScopes = getClientScopes(realm, client, defaultScope);
clientScopes.stream()
.filter(clientScope -> ! existingClientScopes.containsKey(clientScope.getName()))
.filter(clientScope -> Objects.equals(clientScope.getProtocol(), clientProtocol))
.forEach(clientScope -> entity.addClientScope(clientScope.getId(), defaultScope));
}
@Override
public void removeClientScope(RealmModel realm, ClientModel client, ClientScopeModel clientScope) {
MapClientEntity entity = tx.read(UUID.fromString(client.getId()));
if (entity == null) return;
LOG.tracef("removeClientScope(%s, %s, %s)%s", realm, client, clientScope, getShortStackTrace());
entity.removeClientScope(clientScope.getId());
}
@Override
public Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScopes) {
MapClientEntity entity = tx.read(UUID.fromString(client.getId()));
if (entity == null) return null;
// Defaults to openid-connect
String clientProtocol = client.getProtocol() == null ? OIDCLoginProtocol.LOGIN_PROTOCOL : client.getProtocol();
LOG.tracef("getClientScopes(%s, %s, %b)%s", realm, client, defaultScopes, getShortStackTrace());
return entity.getClientScopes(defaultScopes)
.map(clientScopeId -> session.clientScopes().getClientScopeById(realm, clientScopeId))
.filter(Objects::nonNull)
.filter(clientScope -> Objects.equals(clientScope.getProtocol(), clientProtocol))
.collect(Collectors.toMap(ClientScopeModel::getName, Function.identity()));
}
@Override
public void close() {

View file

@ -102,7 +102,7 @@ public class MigrateTo4_0_0 implements Migration {
realm.getClientsStream()
.filter(MigrationUtils::isOIDCNonBearerOnlyClient)
.filter(c -> c.hasScope(offlineAccessRole))
.filter(c -> !c.getClientScopes(false, true).containsKey(OAuth2Constants.OFFLINE_ACCESS))
.filter(c -> !c.getClientScopes(false).containsKey(OAuth2Constants.OFFLINE_ACCESS))
.peek(c -> {
LOG.debugf("Adding client scope 'offline_access' as optional scope to client '%s' in realm '%s'.", c.getClientId(), realm.getName());
c.addClientScope(offlineAccessScope, false);
@ -119,7 +119,7 @@ public class MigrateTo4_0_0 implements Migration {
// Clients with consentRequired, which don't have any client scopes will be added itself to require consent, so that consent screen is shown when users authenticate
realm.getClientsStream()
.filter(ClientModel::isConsentRequired)
.filter(c -> c.getClientScopes(true, true).isEmpty())
.filter(c -> c.getClientScopes(true).isEmpty())
.forEach(c -> {
LOG.debugf("Adding client '%s' of realm '%s' to display itself on consent screen", c.getClientId(), realm.getName());
c.setDisplayOnConsentScreen(true);

View file

@ -704,8 +704,8 @@ public final class KeycloakModelUtils {
public static boolean isClientScopeUsed(RealmModel realm, ClientScopeModel clientScope) {
return realm.getClientsStream()
.filter(c -> (c.getClientScopes(true, false).containsKey(clientScope.getName())) ||
(c.getClientScopes(false, false).containsKey(clientScope.getName())))
.filter(c -> (c.getClientScopes(true).containsKey(clientScope.getName())) ||
(c.getClientScopes(false).containsKey(clientScope.getName())))
.findFirst().isPresent();
}

View file

@ -592,8 +592,8 @@ public class ModelToRepresentation {
rep.setNodeReRegistrationTimeout(clientModel.getNodeReRegistrationTimeout());
rep.setClientAuthenticatorType(clientModel.getClientAuthenticatorType());
rep.setDefaultClientScopes(new LinkedList<>(clientModel.getClientScopes(true, false).keySet()));
rep.setOptionalClientScopes(new LinkedList<>(clientModel.getClientScopes(false, false).keySet()));
rep.setDefaultClientScopes(new LinkedList<>(clientModel.getClientScopes(true).keySet()));
rep.setOptionalClientScopes(new LinkedList<>(clientModel.getClientScopes(false).keySet()));
Set<String> redirectUris = clientModel.getRedirectUris();
if (redirectUris != null) {

View file

@ -1462,12 +1462,12 @@ public class RepresentationToModel {
if (resourceRep.getDefaultClientScopes() != null || resourceRep.getOptionalClientScopes() != null) {
// First remove all default/built in client scopes
for (ClientScopeModel clientScope : client.getClientScopes(true, false).values()) {
for (ClientScopeModel clientScope : client.getClientScopes(true).values()) {
client.removeClientScope(clientScope);
}
// First remove all default/built in client scopes
for (ClientScopeModel clientScope : client.getClientScopes(false, false).values()) {
for (ClientScopeModel clientScope : client.getClientScopes(false).values()) {
client.removeClientScope(clientScope);
}
}
@ -2039,7 +2039,7 @@ public class RepresentationToModel {
// Backwards compatibility. If user had consent for "offline_access" role, we treat it as he has consent for "offline_access" client scope
if (consentRep.getGrantedRealmRoles() != null) {
if (consentRep.getGrantedRealmRoles().contains(OAuth2Constants.OFFLINE_ACCESS)) {
ClientScopeModel offlineScope = client.getClientScopes(false, true).get(OAuth2Constants.OFFLINE_ACCESS);
ClientScopeModel offlineScope = client.getClientScopes(false).get(OAuth2Constants.OFFLINE_ACCESS);
if (offlineScope == null) {
logger.warn("Unable to find offline_access scope referenced in grantedRoles of user");
}

View file

@ -46,8 +46,8 @@ public abstract class AbstractLoginProtocolFactory implements LoginProtocolFacto
factory.register(new ProviderEventListener() {
@Override
public void onEvent(ProviderEvent event) {
if (event instanceof ClientModel.ClientCreationEvent) {
ClientModel client = ((ClientModel.ClientCreationEvent)event).getCreatedClient();
if (event instanceof ClientModel.ClientProtocolUpdatedEvent) {
ClientModel client = ((ClientModel.ClientProtocolUpdatedEvent)event).getClient();
addDefaultClientScopes(client.getRealm(), client);
addDefaults(client);
}

View file

@ -58,6 +58,10 @@ public interface ClientModel extends ClientScopeModel, RoleContainerModel, Prot
KeycloakSession getKeycloakSession();
}
interface ClientProtocolUpdatedEvent extends ProviderEvent {
ClientModel getClient();
}
/**
* Notifies other providers that this client has been updated.
* <p>
@ -221,10 +225,9 @@ public interface ClientModel extends ClientScopeModel, RoleContainerModel, Prot
* Return all default scopes (if 'defaultScope' is true) or all optional scopes (if 'defaultScope' is false) linked with this client
*
* @param defaultScope
* @param filterByProtocol if true, then just client scopes of same protocol like current client will be returned
* @return map where key is the name of the clientScope, value is particular clientScope. Returns empty map if no scopes linked (never returns null).
*/
Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol);
Map<String, ClientScopeModel> getClientScopes(boolean defaultScope);
/**
* <p>Returns a {@link ClientScopeModel} associated with this client.

View file

@ -20,6 +20,7 @@ import org.keycloak.provider.Provider;
import org.keycloak.storage.client.ClientLookupProvider;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -146,4 +147,24 @@ public interface ClientProvider extends ClientLookupProvider, Provider {
* @param realm Realm.
*/
void removeClients(RealmModel realm);
/**
* Assign clientScopes to the client. Add as default scopes (if parameter 'defaultScope' is true)
* or optional scopes (if parameter 'defaultScope' is false)
*
* @param realm Realm.
* @param client Client.
* @param clientScopes to be assigned
* @param defaultScope if true the scopes are assigned as default, or optional in case of false
*/
void addClientScopes(RealmModel realm, ClientModel client, Set<ClientScopeModel> clientScopes, boolean defaultScope);
/**
* Unassign clientScope from the client.
*
* @param realm Realm.
* @param client Client.
* @param clientScope to be unassigned
*/
void removeClientScope(RealmModel realm, ClientModel client, ClientScopeModel clientScope);
}

View file

@ -17,9 +17,11 @@
package org.keycloak.storage.client;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.RealmModel;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -92,4 +94,14 @@ public interface ClientLookupProvider {
* @return Stream of ClientModel or an empty stream if no client is found. Never returns {@code null}.
*/
Stream<ClientModel> searchClientsByClientIdStream(RealmModel realm, String clientId, Integer firstResult, Integer maxResults);
/**
* Return all default scopes (if {@code defaultScope} is {@code true}) or all optional scopes (if {@code defaultScope} is {@code false}) linked with the client
*
* @param realm Realm
* @param client Client
* @param defaultScopes if true default scopes, if false optional scopes, are returned
* @return map where key is the name of the clientScope, value is particular clientScope. Returns empty map if no scopes linked (never returns null).
*/
Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScopes);
}

View file

@ -183,8 +183,8 @@ public class ApplicationsBean {
// Construct scope parameter with all optional scopes to see all potentially available roles
Stream<ClientScopeModel> allClientScopes = Stream.concat(
client.getClientScopes(true, true).values().stream(),
client.getClientScopes(false, true).values().stream());
client.getClientScopes(true).values().stream(),
client.getClientScopes(false).values().stream());
allClientScopes = Stream.concat(allClientScopes, Stream.of(client)).distinct();
Set<RoleModel> availableRoles = TokenManager.getAccess(user, client, allClientScopes);

View file

@ -548,14 +548,14 @@ public class TokenManager {
public static Stream<ClientScopeModel> getRequestedClientScopes(String scopeParam, ClientModel client) {
// Add all default client scopes automatically and client itself
Stream<ClientScopeModel> clientScopes = Stream.concat(
client.getClientScopes(true, true).values().stream(),
client.getClientScopes(true).values().stream(),
Stream.of(client)).distinct();
if (scopeParam == null) {
return clientScopes;
}
Map<String, ClientScopeModel> allOptionalScopes = client.getClientScopes(false, true);
Map<String, ClientScopeModel> allOptionalScopes = client.getClientScopes(false);
// Add optional client scopes requested by scope parameter
return Stream.concat(parseScopeParameter(scopeParam).map(allOptionalScopes::get).filter(Objects::nonNull),
clientScopes).distinct();

View file

@ -72,8 +72,8 @@ public class ClientScopesCondition extends AbstractClientPolicyConditionProvider
private boolean isScopeMatched(String explicitScopes, ClientModel client) {
if (explicitScopes == null) explicitScopes = "";
Collection<String> explicitSpecifiedScopes = new HashSet<>(Arrays.asList(explicitScopes.split(" ")));
Set<String> defaultScopes = client.getClientScopes(true, true).keySet();
Set<String> optionalScopes = client.getClientScopes(false, true).keySet();
Set<String> defaultScopes = client.getClientScopes(true).keySet();
Set<String> optionalScopes = client.getClientScopes(false).keySet();
Set<String> expectedScopes = getScopesForMatching();
if (expectedScopes == null) expectedScopes = new HashSet<>();

View file

@ -73,10 +73,10 @@ public class ClientScopesClientRegistrationPolicy implements ClientRegistrationP
// Allow scopes, which were already presented before
if (requestedDefaultScopeNames != null) {
requestedDefaultScopeNames.removeAll(clientModel.getClientScopes(true, false).keySet());
requestedDefaultScopeNames.removeAll(clientModel.getClientScopes(true).keySet());
}
if (requestedOptionalScopeNames != null) {
requestedOptionalScopeNames.removeAll(clientModel.getClientScopes(false, false).keySet());
requestedOptionalScopeNames.removeAll(clientModel.getClientScopes(false).keySet());
}
List<String> allowedDefaultScopeNames = getAllowedScopeNames(realm, true);

View file

@ -318,7 +318,7 @@ public class ClientResource {
private Stream<ClientScopeRepresentation> getDefaultClientScopes(boolean defaultScope) {
auth.clients().requireView(client);
return client.getClientScopes(defaultScope, true).values().stream().map(ClientResource::toRepresentation);
return client.getClientScopes(defaultScope).values().stream().map(ClientResource::toRepresentation);
}

View file

@ -249,6 +249,7 @@ public class RealmAdminResource {
ClientScopeRepresentation rep = new ClientScopeRepresentation();
rep.setId(clientScope.getId());
rep.setName(clientScope.getName());
rep.setProtocol(clientScope.getProtocol());
return rep;
});
}

View file

@ -16,6 +16,7 @@
*/
package org.keycloak.storage;
import java.util.Map;
import org.jboss.logging.Logger;
import org.keycloak.common.util.reflections.Types;
import org.keycloak.component.ComponentModel;
@ -31,7 +32,9 @@ import org.keycloak.storage.client.ClientStorageProviderModel;
import org.keycloak.utils.ServicesUtils;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
import org.keycloak.models.ClientScopeModel;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -160,6 +163,18 @@ public class ClientStorageManager implements ClientProvider {
return Stream.concat(local, ext);
}
@Override
public Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScopes) {
StorageId storageId = new StorageId(client.getId());
if (storageId.getProviderId() == null) {
return session.clientLocalStorage().getClientScopes(realm, client, defaultScopes);
}
ClientLookupProvider provider = (ClientLookupProvider)getStorageProvider(session, client.getRealm(), storageId.getProviderId());
if (provider == null) return null;
if (!isStorageProviderEnabled(client.getRealm(), storageId.getProviderId())) return null;
return provider.getClientScopes(realm, client, defaultScopes);
}
@Override
public ClientModel addClient(RealmModel realm, String clientId) {
return session.clientLocalStorage().addClient(realm, clientId);
@ -195,6 +210,22 @@ public class ClientStorageManager implements ClientProvider {
session.clientLocalStorage().removeClients(realm);
}
@Override
public void addClientScopes(RealmModel realm, ClientModel client, Set<ClientScopeModel> clientScopes, boolean defaultScope) {
if (!StorageId.isLocalStorage(client.getId())) {
throw new RuntimeException("Federated clients do not support this operation");
}
session.clientLocalStorage().addClientScopes(realm, client, clientScopes, defaultScope);
}
@Override
public void removeClientScope(RealmModel realm, ClientModel client, ClientScopeModel clientScope) {
if (!StorageId.isLocalStorage(client.getId())) {
throw new RuntimeException("Federated clients do not support this operation");
}
session.clientLocalStorage().removeClientScope(realm, client, clientScope);
}
@Override
public void close() {

View file

@ -19,7 +19,10 @@ package org.keycloak.storage.openshift;
import com.openshift.restclient.IClient;
import com.openshift.restclient.NotFoundException;
import com.openshift.restclient.model.IResource;
import java.util.Collections;
import java.util.Map;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.storage.StorageId;
@ -92,4 +95,10 @@ public class OpenshiftClientStorageProvider implements ClientStorageProvider {
return null;
}
}
@Override
public Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScopes) {
// TODO not sure about this, this implementation doesn't use it now
return Collections.EMPTY_MAP;
}
}

View file

@ -275,7 +275,7 @@ public final class OpenshiftSAClientAdapter extends AbstractReadOnlyClientStorag
}
@Override
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol) {
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope) {
if (defaultScope) {
return Collections.emptyMap();
}

View file

@ -92,6 +92,21 @@ public class HardcodedClientStorageProvider implements ClientStorageProvider, Cl
return Stream.empty();
}
@Override
public Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScope) {
if (defaultScope) {
ClientScopeModel rolesScope = KeycloakModelUtils.getClientScopeByName(realm, OIDCLoginProtocolFactory.ROLES_SCOPE);
ClientScopeModel webOriginsScope = KeycloakModelUtils.getClientScopeByName(realm, OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE);
return Arrays.asList(rolesScope, webOriginsScope)
.stream()
.collect(Collectors.toMap(ClientScopeModel::getName, clientScope -> clientScope));
} else {
ClientScopeModel offlineScope = KeycloakModelUtils.getClientScopeByName(realm, "offline_access");
return Collections.singletonMap("offline_access", offlineScope);
}
}
public class ClientAdapter extends AbstractReadOnlyClientStorageAdapter {
public ClientAdapter(RealmModel realm) {
@ -241,18 +256,8 @@ public class HardcodedClientStorageProvider implements ClientStorageProvider, Cl
}
@Override
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol) {
if (defaultScope) {
ClientScopeModel rolesScope = KeycloakModelUtils.getClientScopeByName(realm, OIDCLoginProtocolFactory.ROLES_SCOPE);
ClientScopeModel webOriginsScope = KeycloakModelUtils.getClientScopeByName(realm, OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE);
return Arrays.asList(rolesScope, webOriginsScope)
.stream()
.collect(Collectors.toMap(ClientScopeModel::getName, clientScope -> clientScope));
} else {
ClientScopeModel offlineScope = KeycloakModelUtils.getClientScopeByName(realm, "offline_access");
return Collections.singletonMap("offline_access", offlineScope);
}
public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope) {
return session.clients().getClientScopes(getRealm(), this, defaultScope);
}
@Override

View file

@ -341,6 +341,7 @@ public class ClientScopeTest extends AbstractClientTest {
// Add client scope
ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
scopeRep.setName("foo-scope");
scopeRep.setProtocol("openid-connect");
String scopeId = createClientScope(scopeRep);
// Add client with the clientScope

View file

@ -40,6 +40,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.credential.OTPCredentialModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
@ -112,6 +113,7 @@ public class KcinitTest extends AbstractTestRealmKeycloakTest {
kcinit.setEnabled(true);
kcinit.addRedirectUri("*");
kcinit.setPublicClient(true);
kcinit.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
kcinit.removeRole(realm.getRole(OAuth2Constants.OFFLINE_ACCESS));
ClientModel app = realm.addClient(APP);

View file

@ -382,12 +382,14 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
// Add some clientScopes
ClientScopeRepresentation clientScope = new ClientScopeRepresentation();
clientScope.setName("foo");
clientScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
Response response = realmResource().clientScopes().create(clientScope);
String fooScopeId = ApiUtil.getCreatedId(response);
response.close();
clientScope = new ClientScopeRepresentation();
clientScope.setName("bar");
clientScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
response = realmResource().clientScopes().create(clientScope);
String barScopeId = ApiUtil.getCreatedId(response);
response.close();
@ -436,6 +438,7 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
// Add some clientScope through Admin REST
ClientScopeRepresentation clientScope = new ClientScopeRepresentation();
clientScope.setName("foo");
clientScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
Response response = realmResource().clientScopes().create(clientScope);
String clientScopeId = ApiUtil.getCreatedId(response);
response.close();
@ -475,6 +478,7 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
// Add some clientScope through Admin REST
ClientScopeRepresentation clientScope = new ClientScopeRepresentation();
clientScope.setName("foo");
clientScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
Response response = realmResource().clientScopes().create(clientScope);
String clientScopeId = ApiUtil.getCreatedId(response);
response.close();

View file

@ -55,6 +55,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import static org.keycloak.services.clientregistration.ErrorCodes.INVALID_CLIENT_METADATA;
import static org.keycloak.services.clientregistration.ErrorCodes.INVALID_REDIRECT_URI;
@ -558,10 +559,12 @@ public class ClientRegistrationTest extends AbstractClientRegistrationTest {
@Test
public void registerClientAsAdminWithoutScope() throws ClientRegistrationException {
Set<String> realmDefaultClientScopes = new HashSet<>(adminClient.realm(REALM_NAME).getDefaultDefaultClientScopes()
.stream().map(i->i.getName()).collect(Collectors.toList()));
Set<String> realmOptionalClientScopes = new HashSet<>(adminClient.realm(REALM_NAME).getDefaultOptionalClientScopes()
.stream().map(i->i.getName()).collect(Collectors.toList()));
Set<String> realmDefaultClientScopes = new HashSet<>(adminClient.realm(REALM_NAME).getDefaultDefaultClientScopes().stream()
.filter(scope -> Objects.equals(scope.getProtocol(), OIDCLoginProtocol.LOGIN_PROTOCOL))
.map(i->i.getName()).collect(Collectors.toList()));
Set<String> realmOptionalClientScopes = new HashSet<>(adminClient.realm(REALM_NAME).getDefaultOptionalClientScopes().stream()
.filter(scope -> Objects.equals(scope.getProtocol(), OIDCLoginProtocol.LOGIN_PROTOCOL))
.map(i->i.getName()).collect(Collectors.toList()));
authManageClients();
ClientRepresentation client = new ClientRepresentation();

View file

@ -520,7 +520,9 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
@Test
public void testClientWithoutScope() throws ClientRegistrationException {
Set<String> realmOptionalClientScopes = new HashSet<>(adminClient.realm(REALM_NAME).getDefaultOptionalClientScopes()
.stream().map(i->i.getName()).collect(Collectors.toList()));
.stream()
.filter(scope -> Objects.equals(scope.getProtocol(), OIDCLoginProtocol.LOGIN_PROTOCOL))
.map(i->i.getName()).collect(Collectors.toList()));
OIDCClientRepresentation clientRep = null;
OIDCClientRepresentation response = null;
@ -535,7 +537,9 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
ClientRepresentation rep = clientResource.toRepresentation();
Set<String> realmDefaultClientScopes = new HashSet<>(adminClient.realm(REALM_NAME).getDefaultDefaultClientScopes()
.stream().map(i->i.getName()).collect(Collectors.toList()));
.stream()
.filter(scope -> Objects.equals(scope.getProtocol(), OIDCLoginProtocol.LOGIN_PROTOCOL))
.map(i->i.getName()).collect(Collectors.toList()));
Set<String> registeredDefaultClientScopes = new HashSet<>(rep.getDefaultClientScopes());
assertTrue(realmDefaultClientScopes.equals(new HashSet<>(registeredDefaultClientScopes)));

View file

@ -371,12 +371,12 @@ public class ClientModelTest extends AbstractKeycloakTest {
ClientScopeModel scope1 = scope1Atomic.get();
ClientScopeModel scope2 = scope2Atomic.get();
Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true, true);
Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true);
assertThat("Client Scope contains 'scope1':", clientScopes1.containsKey("scope1"), is(true));
assertThat("Client Scope contains 'scope2':", clientScopes1.containsKey("scope2"), is(false));
assertThat("Client Scope contains 'scope3':", clientScopes1.containsKey("scope3"), is(false));
Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false, true);
Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false);
assertThat("Client Scope contains 'scope1':", clientScopes2.containsKey("scope1"), is(false));
assertThat("Client Scope contains 'scope2':", clientScopes2.containsKey("scope2"), is(true));
assertThat("Client Scope contains 'scope3':", clientScopes2.containsKey("scope3"), is(true));
@ -392,12 +392,12 @@ public class ClientModelTest extends AbstractKeycloakTest {
client = realm.getClientByClientId("templatized");
ClientScopeModel scope3 = scope3Atomic.get();
Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true, true);
Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true);
assertThat("Client Scope contains 'scope1':", clientScopes1.containsKey("scope1"), is(false));
assertThat("Client Scope contains 'scope2':", clientScopes1.containsKey("scope2"), is(false));
assertThat("Client Scope contains 'scope3':", clientScopes1.containsKey("scope3"), is(false));
Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false, true);
Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false);
assertThat("Client Scope contains 'scope1':", clientScopes2.containsKey("scope1"), is(false));
assertThat("Client Scope contains 'scope2':", clientScopes2.containsKey("scope2"), is(false));
assertThat("Client Scope contains 'scope3':", clientScopes2.containsKey("scope3"), is(true));
@ -420,6 +420,7 @@ public class ClientModelTest extends AbstractKeycloakTest {
RealmModel realm = currentSession.realms().getRealmByName(realmName);
client = realm.addClient("templatized");
ClientScopeModel scope1 = realm.addClientScope("template");
scope1.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
scope1Atomic.set(scope1);
client.addClientScope(scope1, true);
});
@ -505,13 +506,13 @@ public class ClientModelTest extends AbstractKeycloakTest {
ClientScopeModel scope2 = scope2Atomic.get();
Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true, true);
Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true);
assertThat("Client Scope contains 'scope1':", clientScopes1.containsKey("scope1"), is(true));
assertThat("Client Scope contains 'scope2':", clientScopes1.containsKey("scope2"), is(false));
assertThat("Client Scope contains 'scope3':", clientScopes1.containsKey("scope3"), is(false));
Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false, true);
Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false);
assertThat("Client Scope contains 'scope1':", clientScopes2.containsKey("scope1"), is(false));
assertThat("Client Scope contains 'scope2':", clientScopes2.containsKey("scope2"), is(true));
assertThat("Client Scope contains 'scope3':", clientScopes2.containsKey("scope3"), is(true));
@ -534,12 +535,12 @@ public class ClientModelTest extends AbstractKeycloakTest {
RealmModel realm = currentSession.realms().getRealmByName(realmName);
client = realm.getClientByClientId("foo2");
Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true, true);
Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true);
assertThat("Client Scope contains 'scope1':", clientScopes1.containsKey("scope1"), is(false));
assertThat("Client Scope contains 'scope2':", clientScopes1.containsKey("scope2"), is(false));
assertThat("Client Scope contains 'scope3':", clientScopes1.containsKey("scope3"), is(false));
Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false, true);
Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false);
assertThat("Client Scope contains 'scope1':", clientScopes2.containsKey("scope1"), is(false));
assertThat("Client Scope contains 'scope2':", clientScopes2.containsKey("scope2"), is(false));
assertThat("Client Scope contains 'scope3':", clientScopes2.containsKey("scope3"), is(true));