KEYCLOAK-14811 Create RoleProvider and make it independent of ClientProvider and RealmProvider

This commit is contained in:
vramik 2020-07-13 00:34:44 +02:00 committed by Pedro Igor
parent 3c4cccd375
commit bfa21c912c
27 changed files with 647 additions and 216 deletions

View file

@ -257,7 +257,7 @@ public class ClientAdapter implements ClientModel, CachedObject {
public Stream<RoleModel> getScopeMappingsStream() {
if (isUpdated()) return updated.getScopeMappingsStream();
return cached.getScope().stream()
.map(id -> cacheSession.getRoleById(id, cachedRealm));
.map(id -> cacheSession.getRoleById(cachedRealm, id));
}
public void addScopeMapping(RoleModel role) {
@ -593,37 +593,37 @@ public class ClientAdapter implements ClientModel, CachedObject {
@Override
public RoleModel getRole(String name) {
return cacheSession.getClientRole(getRealm(), this, name);
return cacheSession.getClientRole(this, name);
}
@Override
public RoleModel addRole(String name) {
return cacheSession.addClientRole(getRealm(), this, name);
return cacheSession.addClientRole(this, name);
}
@Override
public RoleModel addRole(String id, String name) {
return cacheSession.addClientRole(getRealm(), this, id, name);
return cacheSession.addClientRole(this, id, name);
}
@Override
public boolean removeRole(RoleModel role) {
return cacheSession.removeRole(cachedRealm, role);
return cacheSession.removeRole(role);
}
@Override
public Stream<RoleModel> getRolesStream() {
return cacheSession.getClientRolesStream(cachedRealm, this);
return cacheSession.getClientRolesStream(this);
}
@Override
public Stream<RoleModel> getRolesStream(Integer first, Integer max) {
return cacheSession.getClientRolesStream(cachedRealm, this, first, max);
return cacheSession.getClientRolesStream(this, first, max);
}
@Override
public Stream<RoleModel> searchForRolesStream(String search, Integer first, Integer max) {
return cacheSession.searchForClientRolesStream(cachedRealm, this, search, first, max);
return cacheSession.searchForClientRolesStream(this, search, first, max);
}
@Override

View file

@ -160,7 +160,7 @@ public class ClientScopeAdapter implements ClientScopeModel {
public Stream<RoleModel> getScopeMappingsStream() {
if (isUpdated()) return updated.getScopeMappingsStream();
return cached.getScope().stream()
.map(id -> cacheSession.getRoleById(id, cachedRealm));
.map(id -> cacheSession.getRoleById(cachedRealm, id));
}
public void addScopeMapping(RoleModel role) {

View file

@ -202,7 +202,7 @@ public class GroupAdapter implements GroupModel {
if (isUpdated()) return updated.getRoleMappings();
Set<RoleModel> roles = new HashSet<>();
for (String id : cached.getRoleMappings(modelSupplier)) {
RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
RoleModel roleById = keycloakSession.roles().getRoleById(realm, id);
if (roleById == null) {
// chance that role was removed, so just delegate to persistence and get user invalidated
getDelegateForUpdate();

View file

@ -701,7 +701,7 @@ public class RealmAdapter implements CachedRealmModel {
@Override
public RoleModel getRoleById(String id) {
if (isUpdated()) return updated.getRoleById(id);
return cacheSession.getRoleById(id, this);
return cacheSession.getRoleById(this, id);
}
@Override
@ -1049,7 +1049,7 @@ public class RealmAdapter implements CachedRealmModel {
@Override
public boolean removeRole(RoleModel role) {
return cacheSession.removeRole(this, role);
return cacheSession.removeRole(role);
}

View file

@ -101,6 +101,7 @@ public class RealmCacheSession implements CacheRealmProvider {
protected KeycloakSession session;
protected RealmProvider realmDelegate;
protected ClientProvider clientDelegate;
protected RoleProvider roleDelegate;
protected boolean transactionActive;
protected boolean setRollbackOnly;
@ -156,6 +157,13 @@ public class RealmCacheSession implements CacheRealmProvider {
clientDelegate = session.clientStorageManager();
return clientDelegate;
}
public RoleProvider getRoleDelegate() {
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
if (roleDelegate != null) return roleDelegate;
// roleDelegate = session.roleStorageManager();
roleDelegate = session.roleLocalStorage();
return roleDelegate;
}
@ -591,6 +599,7 @@ public class RealmCacheSession implements CacheRealmProvider {
public void close() {
if (realmDelegate != null) realmDelegate.close();
if (clientDelegate != null) clientDelegate.close();
if (roleDelegate != null) roleDelegate.close();
}
@Override
@ -600,7 +609,7 @@ public class RealmCacheSession implements CacheRealmProvider {
@Override
public RoleModel addRealmRole(RealmModel realm, String id, String name) {
RoleModel role = getRealmDelegate().addRealmRole(realm, id, name);
RoleModel role = getRoleDelegate().addRealmRole(realm, id, name);
addedRole(role.getId(), realm.getId());
return role;
}
@ -610,7 +619,7 @@ public class RealmCacheSession implements CacheRealmProvider {
String cacheKey = getRolesCacheKey(realm.getId());
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
if (queryDB) {
return getRealmDelegate().getRealmRolesStream(realm);
return getRoleDelegate().getRealmRolesStream(realm);
}
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
@ -620,8 +629,7 @@ public class RealmCacheSession implements CacheRealmProvider {
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
// intentionally using deprecated method here because role ids needs to be collected for cashing while the stream returned
Set<RoleModel> model = getRealmDelegate().getRealmRoles(realm);
Set<RoleModel> model = getRoleDelegate().getRealmRolesStream(realm).collect(Collectors.toSet());
if (model == null) return null;
Set<String> ids = model.stream().map(RoleModel::getId).collect(Collectors.toSet());
query = new RoleListQuery(loaded, cacheKey, realm, ids);
@ -631,10 +639,10 @@ public class RealmCacheSession implements CacheRealmProvider {
}
Set<RoleModel> list = new HashSet<>();
for (String id : query.getRoles()) {
RoleModel role = session.realms().getRoleById(id, realm);
RoleModel role = session.roles().getRoleById(realm, id);
if (role == null) {
invalidations.add(cacheKey);
return getRealmDelegate().getRealmRolesStream(realm);
return getRoleDelegate().getRealmRolesStream(realm);
}
list.add(role);
}
@ -642,11 +650,11 @@ public class RealmCacheSession implements CacheRealmProvider {
}
@Override
public Stream<RoleModel> getClientRolesStream(RealmModel realm, ClientModel client) {
public Stream<RoleModel> getClientRolesStream(ClientModel client) {
String cacheKey = getRolesCacheKey(client.getId());
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId()) || listInvalidations.contains(realm.getId());
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId()) || listInvalidations.contains(client.getRealm().getId());
if (queryDB) {
return getRealmDelegate().getClientRolesStream(realm, client, null, null);
return getRoleDelegate().getClientRolesStream(client);
}
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
@ -656,21 +664,20 @@ public class RealmCacheSession implements CacheRealmProvider {
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
// intentionally using deprecated method here because role ids needs to be collected for cashing while the stream returned
Set<RoleModel> model = getRealmDelegate().getClientRoles(realm, client, null, null);
Set<RoleModel> model = getRoleDelegate().getClientRolesStream(client).collect(Collectors.toSet());
if (model == null) return null;
Set<String> ids = model.stream().map(RoleModel::getId).collect(Collectors.toSet());
query = new RoleListQuery(loaded, cacheKey, realm, ids, client.getClientId());
query = new RoleListQuery(loaded, cacheKey, client.getRealm(), ids, client.getClientId());
logger.tracev("adding client roles cache miss: client {0} key {1}", client.getClientId(), cacheKey);
cache.addRevisioned(query, startupRevision);
return model.stream();
}
Set<RoleModel> list = new HashSet<>();
for (String id : query.getRoles()) {
RoleModel role = session.realms().getRoleById(id, realm);
RoleModel role = session.roles().getRoleById(client.getRealm(), id);
if (role == null) {
invalidations.add(cacheKey);
return getRealmDelegate().getClientRolesStream(realm, client, null, null);
return getRoleDelegate().getClientRolesStream(client);
}
list.add(role);
}
@ -679,33 +686,32 @@ public class RealmCacheSession implements CacheRealmProvider {
@Override
public Stream<RoleModel> getRealmRolesStream(RealmModel realm, Integer first, Integer max) {
return getRealmDelegate().getRealmRolesStream(realm, first, max);
return getRoleDelegate().getRealmRolesStream(realm, first, max);
}
@Override
public Stream<RoleModel> getClientRolesStream(RealmModel realm, ClientModel client, Integer first, Integer max) {
return getRealmDelegate().getClientRolesStream(realm, client, first, max);
public Stream<RoleModel> getClientRolesStream(ClientModel client, Integer first, Integer max) {
return getRoleDelegate().getClientRolesStream(client, first, max);
}
@Override
public Stream<RoleModel> searchForClientRolesStream(RealmModel realm, ClientModel client, String search, Integer first,
Integer max) {
return getRealmDelegate().searchForClientRolesStream(realm, client, search, first, max);
public Stream<RoleModel> searchForClientRolesStream(ClientModel client, String search, Integer first, Integer max) {
return getRoleDelegate().searchForClientRolesStream(client, search, first, max);
}
@Override
public Stream<RoleModel> searchForRolesStream(RealmModel realm, String search, Integer first, Integer max) {
return getRealmDelegate().searchForRolesStream(realm, search, first, max);
return getRoleDelegate().searchForRolesStream(realm, search, first, max);
}
@Override
public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
public RoleModel addClientRole(ClientModel client, String name) {
return addClientRole(client, KeycloakModelUtils.generateId(), name);
}
@Override
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
RoleModel role = getRealmDelegate().addClientRole(realm, client, id, name);
public RoleModel addClientRole(ClientModel client, String id, String name) {
RoleModel role = getRoleDelegate().addClientRole(client, id, name);
addedRole(role.getId(), client.getId());
return role;
}
@ -715,7 +721,7 @@ public class RealmCacheSession implements CacheRealmProvider {
String cacheKey = getRoleByNameCacheKey(realm.getId(), name);
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
if (queryDB) {
return getRealmDelegate().getRealmRole(realm, name);
return getRoleDelegate().getRealmRole(realm, name);
}
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
@ -725,27 +731,27 @@ public class RealmCacheSession implements CacheRealmProvider {
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
RoleModel model = getRealmDelegate().getRealmRole(realm, name);
RoleModel model = getRoleDelegate().getRealmRole(realm, name);
if (model == null) return null;
query = new RoleListQuery(loaded, cacheKey, realm, model.getId());
logger.tracev("adding realm role cache miss: client {0} key {1}", realm.getName(), cacheKey);
cache.addRevisioned(query, startupRevision);
return model;
}
RoleModel role = getRoleById(query.getRoles().iterator().next(), realm);
RoleModel role = getRoleById(realm, query.getRoles().iterator().next());
if (role == null) {
invalidations.add(cacheKey);
return getRealmDelegate().getRealmRole(realm, name);
return getRoleDelegate().getRealmRole(realm, name);
}
return role;
}
@Override
public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
public RoleModel getClientRole(ClientModel client, String name) {
String cacheKey = getRoleByNameCacheKey(client.getId(), name);
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId()) || listInvalidations.contains(realm.getId());
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId()) || listInvalidations.contains(client.getRealm().getId());
if (queryDB) {
return getRealmDelegate().getClientRole(realm, client, name);
return getRoleDelegate().getClientRole(client, name);
}
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
@ -755,34 +761,44 @@ public class RealmCacheSession implements CacheRealmProvider {
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
RoleModel model = getRealmDelegate().getClientRole(realm, client, name);
RoleModel model = getRoleDelegate().getClientRole(client, name);
if (model == null) return null;
query = new RoleListQuery(loaded, cacheKey, realm, model.getId(), client.getClientId());
query = new RoleListQuery(loaded, cacheKey, client.getRealm(), model.getId(), client.getClientId());
logger.tracev("adding client role cache miss: client {0} key {1}", client.getClientId(), cacheKey);
cache.addRevisioned(query, startupRevision);
return model;
}
RoleModel role = getRoleById(query.getRoles().iterator().next(), realm);
RoleModel role = getRoleById(client.getRealm(), query.getRoles().iterator().next());
if (role == null) {
invalidations.add(cacheKey);
return getRealmDelegate().getClientRole(realm, client, name);
return getRoleDelegate().getClientRole(client, name);
}
return role;
}
@Override
public boolean removeRole(RealmModel realm, RoleModel role) {
public boolean removeRole(RoleModel role) {
listInvalidations.add(role.getContainer().getId());
invalidateRole(role.getId());
invalidationEvents.add(RoleRemovedEvent.create(role.getId(), role.getName(), role.getContainer().getId()));
roleRemovalInvalidations(role.getId(), role.getName(), role.getContainer().getId());
return getRealmDelegate().removeRole(realm, role);
return getRoleDelegate().removeRole(role);
}
@Override
public RoleModel getRoleById(String id, RealmModel realm) {
public void removeRoles(RealmModel realm) {
getRoleDelegate().removeRoles(realm);
}
@Override
public void removeRoles(ClientModel client) {
getRoleDelegate().removeRoles(client);
}
@Override
public RoleModel getRoleById(RealmModel realm, String id) {
CachedRole cached = cache.get(id, CachedRole.class);
if (cached != null && !cached.getRealm().equals(realm.getId())) {
cached = null;
@ -790,7 +806,7 @@ public class RealmCacheSession implements CacheRealmProvider {
if (cached == null) {
Long loaded = cache.getCurrentRevision(id);
RoleModel model = getRealmDelegate().getRoleById(id, realm);
RoleModel model = getRoleDelegate().getRoleById(realm, id);
if (model == null) return null;
if (invalidations.contains(id)) return model;
if (model.isClientRole()) {
@ -801,7 +817,7 @@ public class RealmCacheSession implements CacheRealmProvider {
cache.addRevisioned(cached, startupRevision);
} else if (invalidations.contains(id)) {
return getRealmDelegate().getRoleById(id, realm);
return getRoleDelegate().getRoleById(realm, id);
} else if (managedRoles.containsKey(id)) {
return managedRoles.get(id);
}

View file

@ -70,7 +70,7 @@ public class RoleAdapter implements RoleModel {
protected boolean isUpdated() {
if (updated != null) return true;
if (!invalidated) return false;
updated = cacheSession.getRealmDelegate().getRoleById(cached.getId(), realm);
updated = cacheSession.getRoleDelegate().getRoleById(realm, cached.getId());
if (updated == null) throw new IllegalStateException("Not found in database");
return true;
}
@ -223,7 +223,7 @@ public class RoleAdapter implements RoleModel {
}
private RoleModel getRoleModel() {
return cacheSession.getRealmDelegate().getRoleById(cached.getId(), realm);
return cacheSession.getRoleDelegate().getRoleById(realm, cached.getId());
}
@Override

View file

@ -329,7 +329,7 @@ public class UserAdapter implements CachedUserModel {
if (updated != null) return updated.getRoleMappings();
Set<RoleModel> roles = new HashSet<>();
for (String id : cached.getRoleMappings(modelSupplier)) {
RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
RoleModel roleById = keycloakSession.roles().getRoleById(realm, id);
if (roleById == null) {
// chance that role was removed, so just delete to persistence and get user invalidated
getDelegateForUpdate();

View file

@ -652,37 +652,37 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
@Override
public RoleModel getRole(String name) {
return session.realms().getClientRole(realm, this, name);
return session.roles().getClientRole(this, name);
}
@Override
public RoleModel addRole(String name) {
return session.realms().addClientRole(realm, this, name);
return session.roles().addClientRole(this, name);
}
@Override
public RoleModel addRole(String id, String name) {
return session.realms().addClientRole(realm, this, id, name);
return session.roles().addClientRole(this, id, name);
}
@Override
public boolean removeRole(RoleModel roleModel) {
return session.realms().removeRole(realm, roleModel);
return session.roles().removeRole(roleModel);
}
@Override
public Stream<RoleModel> getRolesStream() {
return session.realms().getClientRolesStream(realm, this, null, null);
return session.roles().getClientRolesStream(this, null, null);
}
@Override
public Stream<RoleModel> getRolesStream(Integer first, Integer max) {
return session.realms().getClientRolesStream(realm, this, first, max);
return session.roles().getClientRolesStream(this, first, max);
}
@Override
public Stream<RoleModel> searchForRolesStream(String search, Integer first, Integer max) {
return session.realms().searchForClientRolesStream(realm, this, search, first, max);
return session.roles().searchForClientRolesStream(this, search, first, max);
}
@Override

View file

@ -32,6 +32,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleContainerModel;
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.ClientScopeEntity;
@ -56,7 +57,7 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaRealmProvider implements RealmProvider, ClientProvider {
public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProvider {
protected static final Logger logger = Logger.getLogger(JpaRealmProvider.class);
private final KeycloakSession session;
protected EntityManager em;
@ -165,10 +166,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider {
adapter.removeClientScope(a.getId());
}
for (RoleModel role : adapter.getRoles()) {
// No need to go through cache. Roles were already invalidated
removeRole(adapter, role);
}
removeRoles(adapter);
for (GroupModel group : adapter.getGroups()) {
session.realms().removeGroup(adapter, group);
@ -231,16 +229,17 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider {
query.setParameter("realm", realm.getId());
List<String> roles = query.getResultList();
if (roles.isEmpty()) return null;
return session.realms().getRoleById(roles.get(0), realm);
return session.roles().getRoleById(realm, roles.get(0));
}
@Override
public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
public RoleModel addClientRole(ClientModel client, String name) {
return addClientRole(client, KeycloakModelUtils.generateId(), name);
}
@Override
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
if (getClientRole(realm, client, name) != null) {
public RoleModel addClientRole(ClientModel client, String id, String name) {
if (getClientRole(client, name) != null) {
throw new ModelDuplicateException();
}
RoleEntity roleEntity = new RoleEntity();
@ -248,9 +247,9 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider {
roleEntity.setName(name);
roleEntity.setClientId(client.getId());
roleEntity.setClientRole(true);
roleEntity.setRealmId(realm.getId());
roleEntity.setRealmId(client.getRealm().getId());
em.persist(roleEntity);
RoleAdapter adapter = new RoleAdapter(session, realm, em, roleEntity);
RoleAdapter adapter = new RoleAdapter(session, client.getRealm(), em, roleEntity);
return adapter;
}
@ -264,13 +263,13 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider {
}
@Override
public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
public RoleModel getClientRole(ClientModel client, String name) {
TypedQuery<String> query = em.createNamedQuery("getClientRoleIdByName", String.class);
query.setParameter("name", name);
query.setParameter("client", client.getId());
List<String> roles = query.getResultList();
if (roles.isEmpty()) return null;
return session.realms().getRoleById(roles.get(0), realm);
return session.roles().getRoleById(client.getRealm(), roles.get(0));
}
@Override
@ -282,13 +281,13 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider {
}
@Override
public Stream<RoleModel> getClientRolesStream(RealmModel realm, ClientModel client, Integer first, Integer max) {
public Stream<RoleModel> getClientRolesStream(ClientModel client, Integer first, Integer max) {
TypedQuery<RoleEntity> query = em.createNamedQuery("getClientRoles", RoleEntity.class);
query.setParameter("client", client.getId());
return getRolesStream(query, realm, first, max);
return getRolesStream(query, client.getRealm(), first, max);
}
protected Stream<RoleModel> getRolesStream(TypedQuery<RoleEntity> query, RealmModel realm, Integer first, Integer max) {
if(Objects.nonNull(first) && Objects.nonNull(max)
&& first >= 0 && max >= 0) {
@ -301,10 +300,10 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider {
}
@Override
public Stream<RoleModel> searchForClientRolesStream(RealmModel realm, ClientModel client, String search, Integer first, Integer max) {
public Stream<RoleModel> searchForClientRolesStream(ClientModel client, String search, Integer first, Integer max) {
TypedQuery<RoleEntity> query = em.createNamedQuery("searchForClientRoles", RoleEntity.class);
query.setParameter("client", client.getId());
return searchForRoles(query, realm, search, first, max);
return searchForRoles(query, client.getRealm(), search, first, max);
}
@Override
@ -329,7 +328,15 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider {
}
@Override
public boolean removeRole(RealmModel realm, RoleModel role) {
public boolean removeRole(RoleModel role) {
RealmModel realm;
if (role.getContainer() instanceof RealmModel) {
realm = (RealmModel) role.getContainer();
} else if (role.getContainer() instanceof ClientModel) {
realm = ((ClientModel)role.getContainer()).getRealm();
} else {
throw new IllegalStateException("RoleModel's container isn not instance of either RealmModel or ClientModel");
}
session.users().preRemove(realm, role);
RoleContainerModel container = role.getContainer();
if (container.getDefaultRoles().contains(role.getName())) {
@ -367,7 +374,19 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider {
}
@Override
public RoleModel getRoleById(String id, RealmModel realm) {
public void removeRoles(RealmModel realm) {
// No need to go through cache. Roles were already invalidated
realm.getRolesStream().forEach(this::removeRole);
}
@Override
public void removeRoles(ClientModel client) {
// No need to go through cache. Roles were already invalidated
client.getRolesStream().forEach(this::removeRole);
}
@Override
public RoleModel getRoleById(RealmModel realm, String id) {
RoleEntity entity = em.find(RoleEntity.class, id);
if (entity == null) return null;
if (!realm.getId().equals(entity.getRealmId())) return null;
@ -681,10 +700,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider {
session.users().preRemove(realm, client);
for (RoleModel role : client.getRoles()) {
// No need to go through cache. Roles were already invalidated
removeRole(realm, role);
}
removeRoles(client);
ClientEntity clientEntity = em.find(ClientEntity.class, id, LockModeType.PESSIMISTIC_WRITE);

View file

@ -0,0 +1,54 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models.jpa;
import org.keycloak.Config;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.RoleProvider;
import org.keycloak.models.RoleProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import javax.persistence.EntityManager;
public class JpaRoleProviderFactory implements RoleProviderFactory {
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return "jpa";
}
@Override
public RoleProvider create(KeycloakSession session) {
EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
return new JpaRealmProvider(session, em);
}
@Override
public void close() {
}
}

View file

@ -50,7 +50,7 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
@Override
public Long getClientsCount() {
return session.realms().getClientsCount(this);
return session.clients().getClientsCount(this);
}
private PasswordPolicy passwordPolicy;
@ -915,42 +915,42 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
@Override
public RoleModel getRole(String name) {
return session.realms().getRealmRole(this, name);
return session.roles().getRealmRole(this, name);
}
@Override
public RoleModel addRole(String name) {
return session.realms().addRealmRole(this, name);
return session.roles().addRealmRole(this, name);
}
@Override
public RoleModel addRole(String id, String name) {
return session.realms().addRealmRole(this, id, name);
return session.roles().addRealmRole(this, id, name);
}
@Override
public boolean removeRole(RoleModel role) {
return session.realms().removeRole(this, role);
return session.roles().removeRole(role);
}
@Override
public Stream<RoleModel> getRolesStream() {
return session.realms().getRealmRolesStream(this);
return session.roles().getRealmRolesStream(this);
}
@Override
public Stream<RoleModel> getRolesStream(Integer first, Integer max) {
return session.realms().getRealmRolesStream(this, first, max);
return session.roles().getRealmRolesStream(this, first, max);
}
@Override
public Stream<RoleModel> searchForRolesStream(String search, Integer first, Integer max) {
return session.realms().searchForRolesStream(this, search, first, max);
return session.roles().searchForRolesStream(this, search, first, max);
}
@Override
public RoleModel getRoleById(String id) {
return session.realms().getRoleById(id, this);
return session.roles().getRoleById(this, id);
}
@Override
@ -2349,4 +2349,4 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
public String toString() {
return String.format("%s@%08x", getId(), hashCode());
}
}
}

View file

@ -0,0 +1,18 @@
#
# Copyright 2020 Red Hat, Inc. and/or its affiliates
# and other contributors as indicated by the @author tags.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
org.keycloak.models.jpa.JpaRoleProviderFactory

View file

@ -63,37 +63,37 @@ public abstract class AbstractClientModel<E extends AbstractEntity> implements C
@Override
public RoleModel getRole(String name) {
return session.realms().getClientRole(realm, this, name);
return session.roles().getClientRole(this, name);
}
@Override
public RoleModel addRole(String name) {
return session.realms().addClientRole(realm, this, name);
return session.roles().addClientRole(this, name);
}
@Override
public RoleModel addRole(String id, String name) {
return session.realms().addClientRole(realm, this, id, name);
return session.roles().addClientRole(this, id, name);
}
@Override
public boolean removeRole(RoleModel role) {
return session.realms().removeRole(realm, role);
return session.roles().removeRole(role);
}
@Override
public Stream<RoleModel> getRolesStream() {
return session.realms().getClientRolesStream(realm, this, null, null);
return session.roles().getClientRolesStream(this, null, null);
}
@Override
public Stream<RoleModel> getRolesStream(Integer firstResult, Integer maxResults) {
return session.realms().getClientRolesStream(realm, this, firstResult, maxResults);
return session.roles().getClientRolesStream(this, firstResult, maxResults);
}
@Override
public Stream<RoleModel> searchForRolesStream(String search, Integer first, Integer max) {
return session.realms().searchForClientRolesStream(realm, this, search, first, max);
return session.roles().searchForClientRolesStream(this, search, first, max);
}
@Override

View file

@ -41,6 +41,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.keycloak.models.map.storage.MapStorage;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
import org.keycloak.models.RoleProvider;
public class MapClientProvider implements ClientProvider {
@ -214,10 +215,7 @@ public class MapClientProvider implements ClientProvider {
final ClientModel client = getClientById(realm, id);
if (client == null) return false;
session.users().preRemove(realm, client);
final RealmProvider realms = session.realms();
for (RoleModel role : client.getRoles()) {
realms.removeRole(realm, role);
}
session.roles().removeRoles(client);
session.getKeycloakSessionFactory().publish(new RealmModel.ClientRemovedEvent() {
@Override

View file

@ -0,0 +1,23 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models;
import org.keycloak.provider.ProviderFactory;
public interface RoleProviderFactory extends ProviderFactory<RoleProvider> {
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
public class RoleSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "role";
}
@Override
public Class<? extends Provider> getProviderClass() {
return RoleProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return RoleProviderFactory.class;
}
}

View file

@ -19,12 +19,13 @@ package org.keycloak.models.cache;
import org.keycloak.models.ClientProvider;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleProvider;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface CacheRealmProvider extends RealmProvider, ClientProvider {
public interface CacheRealmProvider extends RealmProvider, ClientProvider, RoleProvider {
void clear();
RealmProvider getRealmDelegate();

View file

@ -20,6 +20,7 @@ org.keycloak.storage.UserStorageProviderSpi
org.keycloak.storage.federated.UserFederatedStorageProviderSpi
org.keycloak.models.ClientSpi
org.keycloak.models.RealmSpi
org.keycloak.models.RoleSpi
org.keycloak.models.ActionTokenStoreSpi
org.keycloak.models.CodeToTokenStoreSpi
org.keycloak.models.SingleUseTokenStoreSpi
@ -83,4 +84,4 @@ org.keycloak.validation.ClientValidationSPI
org.keycloak.headers.SecurityHeadersSpi
org.keycloak.services.clientpolicy.ClientPolicySpi
org.keycloak.services.clientpolicy.condition.ClientPolicyConditionSpi
org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorSpi
org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorSpi

View file

@ -115,6 +115,15 @@ public interface KeycloakSession {
*/
ClientProvider clients();
/**
* Returns a managed provider instance. Will start a provider transaction. This transaction is managed by the KeycloakSession
* transaction.
*
* @return
* @throws IllegalStateException if transaction is not active
*/
RoleProvider roles();
/**
* Returns a managed provider instance. Will start a provider transaction. This transaction is managed by the KeycloakSession
* transaction.
@ -148,6 +157,8 @@ public interface KeycloakSession {
ClientProvider clientStorageManager();
// RoleProvider roleStorageManager();
/**
* Un-cached view of all users in system including users loaded by UserStorageProviders
*
@ -178,6 +189,13 @@ public interface KeycloakSession {
*/
ClientProvider clientLocalStorage();
/**
* Keycloak specific local storage for roles. No cache in front, this api talks directly to database configured for Keycloak
*
* @return
*/
RoleProvider roleLocalStorage();
/**
* Hybrid storage for UserStorageProviders that can't store a specific piece of keycloak data in their external storage.
* No cache in front.

View file

@ -23,13 +23,12 @@ import org.keycloak.provider.Provider;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface RealmProvider extends Provider /* TODO: Remove in future version */, ClientProvider /* up to here */ {
public interface RealmProvider extends Provider /* TODO: Remove in future version */, ClientProvider, RoleProvider /* up to here */ {
// Note: The reason there are so many query methods here is for layering a cache on top of an persistent KeycloakSession
MigrationModel getMigrationModel();
@ -44,11 +43,6 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
Long getGroupsCount(RealmModel realm, Boolean onlyTopGroups);
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
@Override
long getClientsCount(RealmModel realm);
Long getGroupsCountByNameContaining(RealmModel realm, String search);
List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults);
@ -77,60 +71,9 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
void addTopLevelGroup(RealmModel realm, GroupModel subGroup);
RoleModel addRealmRole(RealmModel realm, String name);
RoleModel addRealmRole(RealmModel realm, String id, String name);
RoleModel getRealmRole(RealmModel realm, String name);
// TODO switch all usages to the stream variant
@Deprecated
default Set<RoleModel> getRealmRoles(RealmModel realm) {
return getRealmRolesStream(realm).collect(Collectors.toSet());
}
Stream<RoleModel> getRealmRolesStream(RealmModel realm);
@Deprecated
default Set<RoleModel> getRealmRoles(RealmModel realm, Integer first, Integer max) {
return getRealmRolesStream(realm, first, max).collect(Collectors.toSet());
}
Stream<RoleModel> getRealmRolesStream(RealmModel realm, Integer first, Integer max);
// TODO switch all usages to the stream variant
@Deprecated
default Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client, Integer first, Integer max) {
return getClientRolesStream(realm, client, first, max).collect(Collectors.toSet());
}
Stream<RoleModel> getClientRolesStream(RealmModel realm, ClientModel client, Integer first, Integer max);
@Deprecated
default Set<RoleModel> searchForClientRoles(RealmModel realm, ClientModel client, String search, Integer first,
Integer max) {
return searchForClientRolesStream(realm, client, search, first, max).collect(Collectors.toSet());
}
Stream<RoleModel> searchForClientRolesStream(RealmModel realm, ClientModel client, String search, Integer first,
Integer max);
@Deprecated
default Set<RoleModel> searchForRoles(RealmModel realm, String search, Integer first, Integer max) {
return searchForRolesStream(realm, search, first, max).collect(Collectors.toSet());
}
Stream<RoleModel> searchForRolesStream(RealmModel realm, String search, Integer first, Integer max);
boolean removeRole(RealmModel realm, RoleModel role);
RoleModel getRoleById(String id, RealmModel realm);
ClientScopeModel getClientScopeById(String id, RealmModel realm);
GroupModel getGroupById(String id, RealmModel realm);
List<RealmModel> getRealms();
List<RealmModel> getRealmsWithProviderType(Class<?> type);
boolean removeRealm(String id);
@ -142,28 +85,10 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
void removeExpiredClientInitialAccess();
void decreaseRemainingCount(RealmModel realm, ClientInitialAccessModel clientInitialAccess); // Separate provider method to ensure we decrease remainingCount atomically instead of doing classic update
/**
* TODO: To be @deprecated Use the corresponding method from {@link ??RoleProvider}. */
default public Stream<RoleModel> getClientRolesStream(RealmModel realm, ClientModel client) {
return getClientRolesStream(realm, client, null, null);
}
/**
* TODO: To be @deprecated Use the corresponding method from {@link ??RoleProvider}. */
public RoleModel getClientRole(RealmModel realm, ClientModel client, String name);
/**
* TODO: To be @deprecated Use the corresponding method from {@link ??RoleProvider}. */
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name);
/**
* TODO: To be @deprecated Use the corresponding method from {@link ??RoleProvider}. */
public RoleModel addClientRole(RealmModel realm, ClientModel client, String name);
// The methods below are going to be removed in future version of Keycloak
// Sadly, we have to copy-paste the declarations from the respective interfaces
// including the "default" body to be able to add a note on deprecation
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
@Override
@ -183,11 +108,18 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
return this.getClients(realm, null, null);
}
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
@Override
default List<ClientModel> getClients(RealmModel realm, Integer firstResult, Integer maxResults) {
return getClientsStream(realm, firstResult, maxResults).collect(Collectors.toList());
}
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
@Override
default List<ClientModel> searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) {
return searchClientsByClientId(clientId, firstResult, maxResults, realm);
return searchClientsByClientIdStream(realm, clientId, firstResult, maxResults).collect(Collectors.toList());
}
/**
@ -203,19 +135,101 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
@Override
public ClientModel getClientByClientId(RealmModel realm, String clientId);
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
@Override
public ClientModel getClientById(RealmModel realm, String id);
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
@Override
public boolean removeClient(RealmModel realm, String id);
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
default boolean removeClient(String id, RealmModel realm) { return this.removeClient(realm, id); }
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
@Override
default List<ClientModel> getAlwaysDisplayInConsoleClients(RealmModel realm) {
return getAlwaysDisplayInConsoleClientsStream(realm).collect(Collectors.toList());
}
/**
* @deprecated Use the corresponding method from {@link ClientProvider}. */
@Override
long getClientsCount(RealmModel realm);
//Role-related methods
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
@Override
default RoleModel addRealmRole(RealmModel realm, String name) { return addRealmRole(realm, null, name); }
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
@Override
RoleModel addRealmRole(RealmModel realm, String id, String name);
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
@Override
RoleModel getRealmRole(RealmModel realm, String name);
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default RoleModel getRoleById(String id, RealmModel realm) {
return getRoleById(realm, id);
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
@Override
default Set<RoleModel> getRealmRoles(RealmModel realm) {
return getRealmRoles(realm, null, null);
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default Set<RoleModel> getRealmRoles(RealmModel realm, Integer first, Integer max) {
return getRealmRolesStream(realm, first, max).collect(Collectors.toSet());
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default Set<RoleModel> searchForRoles(RealmModel realm, String search, Integer first, Integer max) {
return searchForRolesStream(realm, search, first, max).collect(Collectors.toSet());
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default boolean removeRole(RealmModel realm, RoleModel role) {
return removeRole(role);
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
return addClientRole(client, name);
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
return addClientRole(client, id, name);
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
return getClientRole(client, name);
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
return getClientRolesStream(client).collect(Collectors.toSet());
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client, Integer first, Integer max) {
return getClientRolesStream(client, first, max).collect(Collectors.toSet());
}
/**
* @deprecated Use the corresponding method from {@link RoleProvider}. */
default Set<RoleModel> searchForClientRoles(RealmModel realm, ClientModel client, String search, Integer first, Integer max) {
return searchForClientRolesStream(client, search, first, max).collect(Collectors.toSet());
}
}

View file

@ -0,0 +1,186 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.keycloak.provider.Provider;
/**
* Provider of the role records.
* @author vramik
*/
public interface RoleProvider extends Provider {
/**
* Adds a realm role with given {@code name} to the given realm.
* The internal ID of the role will be created automatically.
* @param realm Realm owning this role.
* @param name String name of the role.
* @return Model of the created role.
*/
default RoleModel addRealmRole(RealmModel realm, String name) {
return addRealmRole(realm, null, name);
}
/**
* Adds a realm role with given internal ID and {@code name} to the given realm.
* @param realm Realm owning this role.
* @param id Internal ID of the role or {@code null} if one is to be created by the underlying store
* @param name String name of the role.
* @return Model of the created client.
* @throws IllegalArgumentException If {@code id} does not conform
* the format understood by the underlying store.
*/
RoleModel addRealmRole(RealmModel realm, String id, String name);
/**
* Returns all the realm roles of the given realm.
* Effectively the same as the call {@code getRealmRoles(realm, null, null)}.
* @param realm Realm.
* @return List of the roles. Never returns {@code null}.
* @deprecated use the stream variant instead
*/
@Deprecated
default Set<RoleModel> getRealmRoles(RealmModel realm) {
return getRealmRolesStream(realm, null, null).collect(Collectors.toSet());
}
/**
* Returns all the realm roles of the given realm as a stream.
* Effectively the same as the call {@code getRealmRolesStream(realm, null, null)}.
* @param realm Realm.
* @return Stream of the roles. Never returns {@code null}.
*/
default Stream<RoleModel> getRealmRolesStream(RealmModel realm) {
return getRealmRolesStream(realm, null, null);
}
/**
* Returns the realm roles of the given realm as a stream.
* @param realm Realm.
* @param first First result to return. Ignored if negative or {@code null}.
* @param max Maximum number of results to return. Ignored if negative or {@code null}.
* @return Stream of the roles. Never returns {@code null}.
*/
Stream<RoleModel> getRealmRolesStream(RealmModel realm, Integer first, Integer max);
/**
* Removes given realm role from the given realm.
* @param realm Realm.
* @param role Role to be removed.
* @return {@code true} if the role existed and has been removed, {@code false} otherwise.
*/
boolean removeRole(RoleModel role);
/**
* Removes all roles from the given realm.
* @param realm Realm.
*/
void removeRoles(RealmModel realm);
/**
* Adds a client role with given {@code name} to the given client.
* The internal ID of the role will be created automatically.
* @param client Client owning this role.
* @param name String name of the role.
* @return Model of the created role.
*/
RoleModel addClientRole(ClientModel client, String name);
/**
* Adds a client role with given internal ID and {@code name} to the given client.
* @param client Client owning this role.
* @param id Internal ID of the client role or {@code null} if one is to be created by the underlying store.
* @param name String name of the role.
* @return Model of the created role.
*/
RoleModel addClientRole(ClientModel client, String id, String name);
/**
* Returns all the client roles of the given client.
* Effectively the same as the call {@code getClientRoles(client, null, null)}.
* @param client Client.
* @return List of the roles. Never returns {@code null}.
*/
default Stream<RoleModel> getClientRolesStream(ClientModel client) {
return getClientRolesStream(client, null, null);
}
/**
* Returns the client roles of the given client.
* @param client Client.
* @param first First result to return. Ignored if negative or {@code null}.
* @param max Maximum number of results to return. Ignored if negative or {@code null}.
* @return List of the roles. Never returns {@code null}.
*/
Stream<RoleModel> getClientRolesStream(ClientModel client, Integer first, Integer max);
/**
* Removes all roles from the given client.
* @param client Client.
*/
void removeRoles(ClientModel client);
//TODO RoleLookupProvider
/**
* Exact search for a role by given name.
* @param realm Realm.
* @param name String name of the role.
* @return Model of the role, or {@code null} if no role is found.
*/
RoleModel getRealmRole(RealmModel realm, String name);
/**
* Exact search for a role by its internal ID..
* @param realm Realm.
* @param id Internal ID of the role.
* @return Model of the role.
*/
RoleModel getRoleById(RealmModel realm, String id);
/**
* Case-insensitive search for roles that contain the given string in their name or description.
* @param realm Realm.
* @param search Searched substring of the role's name or description.
* @param first First result to return. Ignored if negative or {@code null}.
* @param max Maximum number of results to return. Ignored if negative or {@code null}.
* @return Stream of the realm roles their name or description contains given search string.
* Never returns {@code null}.
*/
Stream<RoleModel> searchForRolesStream(RealmModel realm, String search, Integer first, Integer max);
/**
* Exact search for a client role by given name.
* @param client Client.
* @param name String name of the role.
* @return Model of the role, or {@code null} if no role is found.
*/
RoleModel getClientRole(ClientModel client, String name);
/**
* Case-insensitive search for client roles that contain the given string in their name or description.
* @param client Client.
* @param search String to search by role's name or description.
* @param first First result to return. Ignored if negative or {@code null}.
* @param max Maximum number of results to return. Ignored if negative or {@code null}.
* @return Stream of the client roles their name or description contains given search string.
* Never returns {@code null}.
*/
Stream<RoleModel> searchForClientRolesStream(ClientModel client, String search, Integer first, Integer max);
}

View file

@ -29,6 +29,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakTransactionManager;
import org.keycloak.models.KeyManager;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleProvider;
import org.keycloak.models.ThemeManager;
import org.keycloak.models.UserCredentialManager;
import org.keycloak.models.UserProvider;
@ -41,6 +42,7 @@ import org.keycloak.services.clientpolicy.ClientPolicyManager;
import org.keycloak.services.clientpolicy.DefaultClientPolicyManager;
import org.keycloak.sessions.AuthenticationSessionProvider;
import org.keycloak.storage.ClientStorageManager;
//import org.keycloak.storage.RoleStorageManager;
import org.keycloak.storage.UserStorageManager;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.vault.DefaultVaultTranscriber;
@ -67,8 +69,10 @@ public class DefaultKeycloakSession implements KeycloakSession {
private final Map<String, Object> attributes = new HashMap<>();
private RealmProvider model;
private ClientProvider clientProvider;
private RoleProvider roleProvider;
private UserStorageManager userStorageManager;
private ClientStorageManager clientStorageManager;
// private RoleStorageManager roleStorageManager;
private UserCredentialStoreManager userCredentialStorageManager;
private UserSessionProvider sessionProvider;
private AuthenticationSessionProvider authenticationSessionProvider;
@ -110,6 +114,17 @@ public class DefaultKeycloakSession implements KeycloakSession {
}
}
private RoleProvider getRoleProvider() {
// TODO: Extract RoleProvider from CacheRealmProvider and use that instead
RoleProvider cache = getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
// return roleStorageManager();
return roleLocalStorage();
}
}
@Override
public UserCache userCache() {
return getProvider(UserCache.class);
@ -184,6 +199,17 @@ public class DefaultKeycloakSession implements KeycloakSession {
return clientStorageManager;
}
@Override
public RoleProvider roleLocalStorage() {
return getProvider(RoleProvider.class);
}
// @Override
// public RoleProvider roleStorageManager() {
// if (roleStorageManager == null) roleStorageManager = new RoleStorageManager(this);
// return roleStorageManager;
// }
@Override
public UserProvider userStorageManager() {
@ -296,6 +322,14 @@ public class DefaultKeycloakSession implements KeycloakSession {
return clientProvider;
}
@Override
public RoleProvider roles() {
if (roleProvider == null) {
roleProvider = getRoleProvider();
}
return roleProvider;
}
@Override
public UserSessionProvider sessions() {

View file

@ -139,7 +139,7 @@ public class ClientModelTest extends AbstractKeycloakTest {
assertThat("Realm Model 'original' is NULL !!", realm, notNullValue());
ClientModel from = realm.getClientByClientId("from");
RoleModel role = currentSession.realms().getRoleById(roleId, realm);
RoleModel role = currentSession.roles().getRoleById(realm, roleId);
from.removeRole(role);
currentSession.clients().removeClient(realm, from.getId());
@ -185,7 +185,7 @@ public class ClientModelTest extends AbstractKeycloakTest {
ClientModel scoped = realm.getClientByClientId("scoped");
ClientModel from = realm.getClientByClientId("from");
RoleModel role = currentSession.realms().getRoleById(roleId, realm);
RoleModel role = currentSession.roles().getRoleById(realm, roleId);
from.removeRole(role);
Set<RoleModel> scopeMappings = scoped.getScopeMappings();
@ -216,7 +216,7 @@ public class ClientModelTest extends AbstractKeycloakTest {
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRealmRoleRemove2) -> {
currentSession = sessionRealmRoleRemove2;
RealmModel realm = currentSession.realms().getRealmByName(realmName);
RoleModel role = currentSession.realms().getRoleById(roleId, realm);
RoleModel role = currentSession.roles().getRoleById(realm, roleId);
realm.removeRole(role);
});

View file

@ -35,6 +35,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.RoleProvider;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
@ -387,7 +388,7 @@ public class OwnerReplacementTest extends AbstractKeycloakTest {
// Get ID of some object from realm1
((session, realm1) -> {
RoleModel role = session.getProvider(RealmProvider.class).addRealmRole(realm1, "foo");
RoleModel role = session.getProvider(RoleProvider.class).addRealmRole(realm1, "foo");
realm1.addDefaultRole("foo");
return role.getId();
@ -395,7 +396,7 @@ public class OwnerReplacementTest extends AbstractKeycloakTest {
// Test lookup realm1 object in realm2 should not work
((session, realm2, realm1RoleId) -> {
RoleModel role = session.getProvider(RealmProvider.class).getRoleById(realm1RoleId, realm2);
RoleModel role = session.getProvider(RoleProvider.class).getRoleById(realm2, realm1RoleId);
Assert.assertNull(role);
}),
@ -414,16 +415,13 @@ public class OwnerReplacementTest extends AbstractKeycloakTest {
// Try remove object from realm1 in the context of realm2
((session, realm1, realm2, realm1RoleId) -> {
RoleModel role = session.getProvider(RealmProvider.class).getRoleById(realm1RoleId, realm1);
session.getProvider(RealmProvider.class).removeRole(realm2, role);
// not possible to remove object from realm1 in the context of realm2 any more
}),
// Test remove from above was not successful
((session, realm1, realm1RoleId) -> {
RoleModel role = session.getProvider(RealmProvider.class).getRoleById(realm1RoleId, realm1);
Assert.assertNotNull(role);
Assert.assertTrue(realm1.getDefaultRoles().contains("foo"));
// nothing to test
})
);

View file

@ -48,6 +48,10 @@
"provider": "${keycloak.client.provider:jpa}"
},
"role": {
"provider": "${keycloak.role.provider:jpa}"
},
"mapStorage": {
"provider": "${keycloak.mapStorage.provider:concurrenthashmap}",
"concurrenthashmap": {

View file

@ -72,9 +72,9 @@ public class TestCacheUtils {
realm.getRoleById(role.getId());
roleContainer.getRole(role.getName());
if (roleContainer instanceof RealmModel) {
session.realms().getRealmRole(realm, role.getName());
session.roles().getRealmRole(realm, role.getName());
} else {
session.realms().getClientRole(realm, (ClientModel) roleContainer, role.getName());
session.roles().getClientRole((ClientModel) roleContainer, role.getName());
}
}
}

View file

@ -22,6 +22,10 @@
"provider": "${keycloak.client.provider:jpa}"
},
"role": {
"provider": "${keycloak.role.provider:jpa}"
},
"mapStorage": {
"provider": "${keycloak.mapStorage.provider:concurrenthashmap}",
"concurrenthashmap": {