KEYCLOAK-14972 Add independent GroupProvider interface
This commit is contained in:
parent
fdcfa6e13e
commit
ae39760a62
22 changed files with 545 additions and 96 deletions
|
@ -123,7 +123,7 @@ public class SSSDFederationProvider implements UserStorageProvider,
|
||||||
for (String s : sssd.getGroups()) {
|
for (String s : sssd.getGroups()) {
|
||||||
GroupModel group = KeycloakModelUtils.findGroupByPath(realm, "/" + s);
|
GroupModel group = KeycloakModelUtils.findGroupByPath(realm, "/" + s);
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
group = session.realms().createGroup(realm, s);
|
group = session.groups().createGroup(realm, s);
|
||||||
}
|
}
|
||||||
user.joinGroup(group);
|
user.joinGroup(group);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ public class GroupAdapter implements GroupModel {
|
||||||
protected boolean isUpdated() {
|
protected boolean isUpdated() {
|
||||||
if (updated != null) return true;
|
if (updated != null) return true;
|
||||||
if (!invalidated) return false;
|
if (!invalidated) return false;
|
||||||
updated = cacheSession.getRealmDelegate().getGroupById(cached.getId(), realm);
|
updated = cacheSession.getGroupDelegate().getGroupById(realm, cached.getId());
|
||||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -224,7 +224,7 @@ public class GroupAdapter implements GroupModel {
|
||||||
public GroupModel getParent() {
|
public GroupModel getParent() {
|
||||||
if (isUpdated()) return updated.getParent();
|
if (isUpdated()) return updated.getParent();
|
||||||
if (cached.getParentId() == null) return null;
|
if (cached.getParentId() == null) return null;
|
||||||
return keycloakSession.realms().getGroupById(cached.getParentId(), realm);
|
return keycloakSession.groups().getGroupById(realm, cached.getParentId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -238,7 +238,7 @@ public class GroupAdapter implements GroupModel {
|
||||||
if (isUpdated()) return updated.getSubGroups();
|
if (isUpdated()) return updated.getSubGroups();
|
||||||
Set<GroupModel> subGroups = new HashSet<>();
|
Set<GroupModel> subGroups = new HashSet<>();
|
||||||
for (String id : cached.getSubGroups(modelSupplier)) {
|
for (String id : cached.getSubGroups(modelSupplier)) {
|
||||||
GroupModel subGroup = keycloakSession.realms().getGroupById(id, realm);
|
GroupModel subGroup = keycloakSession.groups().getGroupById(realm, id);
|
||||||
if (subGroup == null) {
|
if (subGroup == null) {
|
||||||
// chance that role was removed, so just delegate to persistence and get user invalidated
|
// chance that role was removed, so just delegate to persistence and get user invalidated
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
|
@ -273,6 +273,6 @@ public class GroupAdapter implements GroupModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupModel getGroupModel() {
|
private GroupModel getGroupModel() {
|
||||||
return cacheSession.getRealmDelegate().getGroupById(cached.getId(), realm);
|
return cacheSession.getGroupDelegate().getGroupById(realm, cached.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -710,7 +710,7 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
|
|
||||||
List<GroupModel> defaultGroups = new LinkedList<>();
|
List<GroupModel> defaultGroups = new LinkedList<>();
|
||||||
for (String id : cached.getDefaultGroups()) {
|
for (String id : cached.getDefaultGroups()) {
|
||||||
defaultGroups.add(cacheSession.getGroupById(id, this));
|
defaultGroups.add(cacheSession.getGroupById(this, id));
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(defaultGroups);
|
return Collections.unmodifiableList(defaultGroups);
|
||||||
|
|
||||||
|
@ -1416,7 +1416,7 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupModel getGroupById(String id) {
|
public GroupModel getGroupById(String id) {
|
||||||
return cacheSession.getGroupById(id, this);
|
return cacheSession.getGroupById(this, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -101,6 +101,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
protected KeycloakSession session;
|
protected KeycloakSession session;
|
||||||
protected RealmProvider realmDelegate;
|
protected RealmProvider realmDelegate;
|
||||||
protected ClientProvider clientDelegate;
|
protected ClientProvider clientDelegate;
|
||||||
|
protected GroupProvider groupDelegate;
|
||||||
protected RoleProvider roleDelegate;
|
protected RoleProvider roleDelegate;
|
||||||
protected boolean transactionActive;
|
protected boolean transactionActive;
|
||||||
protected boolean setRollbackOnly;
|
protected boolean setRollbackOnly;
|
||||||
|
@ -163,8 +164,12 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
roleDelegate = session.roleStorageManager();
|
roleDelegate = session.roleStorageManager();
|
||||||
return roleDelegate;
|
return roleDelegate;
|
||||||
}
|
}
|
||||||
|
public GroupProvider getGroupDelegate() {
|
||||||
|
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||||
|
if (groupDelegate != null) return groupDelegate;
|
||||||
|
groupDelegate = session.groupLocalStorage();
|
||||||
|
return groupDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -826,7 +831,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupModel getGroupById(String id, RealmModel realm) {
|
public GroupModel getGroupById(RealmModel realm, String id) {
|
||||||
CachedGroup cached = cache.get(id, CachedGroup.class);
|
CachedGroup cached = cache.get(id, CachedGroup.class);
|
||||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||||
cached = null;
|
cached = null;
|
||||||
|
@ -834,14 +839,14 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
if (cached == null) {
|
if (cached == null) {
|
||||||
Long loaded = cache.getCurrentRevision(id);
|
Long loaded = cache.getCurrentRevision(id);
|
||||||
GroupModel model = getRealmDelegate().getGroupById(id, realm);
|
GroupModel model = getGroupDelegate().getGroupById(realm, id);
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
if (invalidations.contains(id)) return model;
|
if (invalidations.contains(id)) return model;
|
||||||
cached = new CachedGroup(loaded, realm, model);
|
cached = new CachedGroup(loaded, realm, model);
|
||||||
cache.addRevisioned(cached, startupRevision);
|
cache.addRevisioned(cached, startupRevision);
|
||||||
|
|
||||||
} else if (invalidations.contains(id)) {
|
} else if (invalidations.contains(id)) {
|
||||||
return getRealmDelegate().getGroupById(id, realm);
|
return getGroupDelegate().getGroupById(realm, id);
|
||||||
} else if (managedGroups.containsKey(id)) {
|
} else if (managedGroups.containsKey(id)) {
|
||||||
return managedGroups.get(id);
|
return managedGroups.get(id);
|
||||||
}
|
}
|
||||||
|
@ -857,7 +862,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
listInvalidations.add(realm.getId());
|
listInvalidations.add(realm.getId());
|
||||||
|
|
||||||
invalidationEvents.add(GroupMovedEvent.create(group, toParent, realm.getId()));
|
invalidationEvents.add(GroupMovedEvent.create(group, toParent, realm.getId()));
|
||||||
getRealmDelegate().moveGroup(realm, group, toParent);
|
getGroupDelegate().moveGroup(realm, group, toParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -865,7 +870,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
String cacheKey = getGroupsQueryCacheKey(realm.getId());
|
String cacheKey = getGroupsQueryCacheKey(realm.getId());
|
||||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||||
if (queryDB) {
|
if (queryDB) {
|
||||||
return getRealmDelegate().getGroups(realm);
|
return getGroupDelegate().getGroups(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
||||||
|
@ -875,7 +880,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||||
List<GroupModel> model = getRealmDelegate().getGroups(realm);
|
List<GroupModel> model = getGroupDelegate().getGroups(realm);
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
Set<String> ids = new HashSet<>();
|
Set<String> ids = new HashSet<>();
|
||||||
for (GroupModel client : model) ids.add(client.getId());
|
for (GroupModel client : model) ids.add(client.getId());
|
||||||
|
@ -886,10 +891,10 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
}
|
}
|
||||||
List<GroupModel> list = new LinkedList<>();
|
List<GroupModel> list = new LinkedList<>();
|
||||||
for (String id : query.getGroups()) {
|
for (String id : query.getGroups()) {
|
||||||
GroupModel group = session.realms().getGroupById(id, realm);
|
GroupModel group = session.groups().getGroupById(realm, id);
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
invalidations.add(cacheKey);
|
invalidations.add(cacheKey);
|
||||||
return getRealmDelegate().getGroups(realm);
|
return getGroupDelegate().getGroups(realm);
|
||||||
}
|
}
|
||||||
list.add(group);
|
list.add(group);
|
||||||
}
|
}
|
||||||
|
@ -901,7 +906,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getGroupsCount(RealmModel realm, Boolean onlyTopGroups) {
|
public Long getGroupsCount(RealmModel realm, Boolean onlyTopGroups) {
|
||||||
return getRealmDelegate().getGroupsCount(realm, onlyTopGroups);
|
return getGroupDelegate().getGroupsCount(realm, onlyTopGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -911,12 +916,12 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getGroupsCountByNameContaining(RealmModel realm, String search) {
|
public Long getGroupsCountByNameContaining(RealmModel realm, String search) {
|
||||||
return getRealmDelegate().getGroupsCountByNameContaining(realm, search);
|
return getGroupDelegate().getGroupsCountByNameContaining(realm, search);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
|
public List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
|
||||||
return getRealmDelegate().getGroupsByRole(realm, role, firstResult, maxResults);
|
return getGroupDelegate().getGroupsByRole(realm, role, firstResult, maxResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -924,7 +929,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
String cacheKey = getTopGroupsQueryCacheKey(realm.getId());
|
String cacheKey = getTopGroupsQueryCacheKey(realm.getId());
|
||||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||||
if (queryDB) {
|
if (queryDB) {
|
||||||
return getRealmDelegate().getTopLevelGroups(realm);
|
return getGroupDelegate().getTopLevelGroups(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
||||||
|
@ -934,7 +939,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||||
List<GroupModel> model = getRealmDelegate().getTopLevelGroups(realm);
|
List<GroupModel> model = getGroupDelegate().getTopLevelGroups(realm);
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
Set<String> ids = new HashSet<>();
|
Set<String> ids = new HashSet<>();
|
||||||
for (GroupModel client : model) ids.add(client.getId());
|
for (GroupModel client : model) ids.add(client.getId());
|
||||||
|
@ -945,10 +950,10 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
}
|
}
|
||||||
List<GroupModel> list = new LinkedList<>();
|
List<GroupModel> list = new LinkedList<>();
|
||||||
for (String id : query.getGroups()) {
|
for (String id : query.getGroups()) {
|
||||||
GroupModel group = session.realms().getGroupById(id, realm);
|
GroupModel group = session.groups().getGroupById(realm, id);
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
invalidations.add(cacheKey);
|
invalidations.add(cacheKey);
|
||||||
return getRealmDelegate().getTopLevelGroups(realm);
|
return getGroupDelegate().getTopLevelGroups(realm);
|
||||||
}
|
}
|
||||||
list.add(group);
|
list.add(group);
|
||||||
}
|
}
|
||||||
|
@ -963,7 +968,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
String cacheKey = getTopGroupsQueryCacheKey(realm.getId() + first + max);
|
String cacheKey = getTopGroupsQueryCacheKey(realm.getId() + first + max);
|
||||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId() + first + max);
|
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId() + first + max);
|
||||||
if (queryDB) {
|
if (queryDB) {
|
||||||
return getRealmDelegate().getTopLevelGroups(realm, first, max);
|
return getGroupDelegate().getTopLevelGroups(realm, first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
|
||||||
|
@ -973,7 +978,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
if (Objects.isNull(query)) {
|
if (Objects.isNull(query)) {
|
||||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||||
List<GroupModel> model = getRealmDelegate().getTopLevelGroups(realm, first, max);
|
List<GroupModel> model = getGroupDelegate().getTopLevelGroups(realm, first, max);
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
Set<String> ids = new HashSet<>();
|
Set<String> ids = new HashSet<>();
|
||||||
for (GroupModel client : model) ids.add(client.getId());
|
for (GroupModel client : model) ids.add(client.getId());
|
||||||
|
@ -984,10 +989,10 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
}
|
}
|
||||||
List<GroupModel> list = new LinkedList<>();
|
List<GroupModel> list = new LinkedList<>();
|
||||||
for (String id : query.getGroups()) {
|
for (String id : query.getGroups()) {
|
||||||
GroupModel group = session.realms().getGroupById(id, realm);
|
GroupModel group = session.groups().getGroupById(realm, id);
|
||||||
if (Objects.isNull(group)) {
|
if (Objects.isNull(group)) {
|
||||||
invalidations.add(cacheKey);
|
invalidations.add(cacheKey);
|
||||||
return getRealmDelegate().getTopLevelGroups(realm);
|
return getGroupDelegate().getTopLevelGroups(realm);
|
||||||
}
|
}
|
||||||
list.add(group);
|
list.add(group);
|
||||||
}
|
}
|
||||||
|
@ -999,7 +1004,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max) {
|
public List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer first, Integer max) {
|
||||||
return getRealmDelegate().searchForGroupByName(realm, search, first, max);
|
return getGroupDelegate().searchForGroupByName(realm, search, first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1013,7 +1018,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
invalidationEvents.add(GroupRemovedEvent.create(group, realm.getId()));
|
invalidationEvents.add(GroupRemovedEvent.create(group, realm.getId()));
|
||||||
|
|
||||||
return getRealmDelegate().removeGroup(realm, group);
|
return getGroupDelegate().removeGroup(realm, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupModel groupAdded(RealmModel realm, GroupModel group, GroupModel toParent) {
|
private GroupModel groupAdded(RealmModel realm, GroupModel group, GroupModel toParent) {
|
||||||
|
@ -1027,7 +1032,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent) {
|
public GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent) {
|
||||||
GroupModel group = getRealmDelegate().createGroup(realm, id, name, toParent);
|
GroupModel group = getGroupDelegate().createGroup(realm, id, name, toParent);
|
||||||
return groupAdded(realm, group, toParent);
|
return groupAdded(realm, group, toParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1040,7 +1045,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
addGroupEventIfAbsent(GroupMovedEvent.create(subGroup, null, realm.getId()));
|
addGroupEventIfAbsent(GroupMovedEvent.create(subGroup, null, realm.getId()));
|
||||||
|
|
||||||
getRealmDelegate().addTopLevelGroup(realm, subGroup);
|
getGroupDelegate().addTopLevelGroup(realm, subGroup);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -352,7 +352,7 @@ public class UserAdapter implements CachedUserModel {
|
||||||
if (updated != null) return updated.getGroups();
|
if (updated != null) return updated.getGroups();
|
||||||
Set<GroupModel> groups = new LinkedHashSet<>();
|
Set<GroupModel> groups = new LinkedHashSet<>();
|
||||||
for (String id : cached.getGroups(modelSupplier)) {
|
for (String id : cached.getGroups(modelSupplier)) {
|
||||||
GroupModel groupModel = keycloakSession.realms().getGroupById(id, realm);
|
GroupModel groupModel = keycloakSession.groups().getGroupById(realm, id);
|
||||||
if (groupModel == null) {
|
if (groupModel == null) {
|
||||||
// chance that role was removed, so just delete to persistence and get user invalidated
|
// chance that role was removed, so just delete to persistence and get user invalidated
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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.GroupProvider;
|
||||||
|
import org.keycloak.models.GroupProviderFactory;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
public class JpaGroupProviderFactory implements GroupProviderFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "jpa";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupProvider create(KeycloakSession session) {
|
||||||
|
EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
||||||
|
return new JpaRealmProvider(session, em);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientProvider;
|
import org.keycloak.models.ClientProvider;
|
||||||
import org.keycloak.models.ClientScopeModel;
|
import org.keycloak.models.ClientScopeModel;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.GroupProvider;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -57,7 +58,7 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProvider {
|
public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupProvider, RoleProvider {
|
||||||
protected static final Logger logger = Logger.getLogger(JpaRealmProvider.class);
|
protected static final Logger logger = Logger.getLogger(JpaRealmProvider.class);
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
protected EntityManager em;
|
protected EntityManager em;
|
||||||
|
@ -169,7 +170,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProv
|
||||||
removeRoles(adapter);
|
removeRoles(adapter);
|
||||||
|
|
||||||
for (GroupModel group : adapter.getGroups()) {
|
for (GroupModel group : adapter.getGroups()) {
|
||||||
session.realms().removeGroup(adapter, group);
|
session.groups().removeGroup(adapter, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
num = em.createNamedQuery("removeClientInitialAccessByRealm")
|
num = em.createNamedQuery("removeClientInitialAccessByRealm")
|
||||||
|
@ -395,7 +396,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProv
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupModel getGroupById(String id, RealmModel realm) {
|
public GroupModel getGroupById(RealmModel realm, String id) {
|
||||||
GroupEntity groupEntity = em.find(GroupEntity.class, id);
|
GroupEntity groupEntity = em.find(GroupEntity.class, id);
|
||||||
if (groupEntity == null) return null;
|
if (groupEntity == null) return null;
|
||||||
if (!groupEntity.getRealm().equals(realm.getId())) return null;
|
if (!groupEntity.getRealm().equals(realm.getId())) return null;
|
||||||
|
@ -413,7 +414,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProv
|
||||||
}
|
}
|
||||||
group.setParent(toParent);
|
group.setParent(toParent);
|
||||||
if (toParent != null) toParent.addChild(group);
|
if (toParent != null) toParent.addChild(group);
|
||||||
else session.realms().addTopLevelGroup(realm, group);
|
else session.groups().addTopLevelGroup(realm, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -421,7 +422,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProv
|
||||||
RealmEntity ref = em.getReference(RealmEntity.class, realm.getId());
|
RealmEntity ref = em.getReference(RealmEntity.class, realm.getId());
|
||||||
|
|
||||||
return ref.getGroups().stream()
|
return ref.getGroups().stream()
|
||||||
.map(g -> session.realms().getGroupById(g.getId(), realm))
|
.map(g -> session.groups().getGroupById(realm, g.getId()))
|
||||||
.sorted(Comparator.comparing(GroupModel::getName))
|
.sorted(Comparator.comparing(GroupModel::getName))
|
||||||
.collect(Collectors.collectingAndThen(
|
.collect(Collectors.collectingAndThen(
|
||||||
Collectors.toList(), Collections::unmodifiableList));
|
Collectors.toList(), Collections::unmodifiableList));
|
||||||
|
@ -479,7 +480,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProv
|
||||||
|
|
||||||
return ref.getGroups().stream()
|
return ref.getGroups().stream()
|
||||||
.filter(g -> GroupEntity.TOP_PARENT_ID.equals(g.getParentId()))
|
.filter(g -> GroupEntity.TOP_PARENT_ID.equals(g.getParentId()))
|
||||||
.map(g -> session.realms().getGroupById(g.getId(), realm))
|
.map(g -> session.groups().getGroupById(realm, g.getId()))
|
||||||
.sorted(Comparator.comparing(GroupModel::getName))
|
.sorted(Comparator.comparing(GroupModel::getName))
|
||||||
.collect(Collectors.collectingAndThen(
|
.collect(Collectors.collectingAndThen(
|
||||||
Collectors.toList(), Collections::unmodifiableList));
|
Collectors.toList(), Collections::unmodifiableList));
|
||||||
|
@ -496,7 +497,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProv
|
||||||
List<GroupModel> list = new ArrayList<>();
|
List<GroupModel> list = new ArrayList<>();
|
||||||
if(Objects.nonNull(groupIds) && !groupIds.isEmpty()) {
|
if(Objects.nonNull(groupIds) && !groupIds.isEmpty()) {
|
||||||
for (String id : groupIds) {
|
for (String id : groupIds) {
|
||||||
GroupModel group = getGroupById(id, realm);
|
GroupModel group = getGroupById(realm, id);
|
||||||
list.add(group);
|
list.add(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -532,7 +533,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProv
|
||||||
|
|
||||||
realm.removeDefaultGroup(group);
|
realm.removeDefaultGroup(group);
|
||||||
for (GroupModel subGroup : group.getSubGroups()) {
|
for (GroupModel subGroup : group.getSubGroups()) {
|
||||||
session.realms().removeGroup(realm, subGroup);
|
session.groups().removeGroup(realm, subGroup);
|
||||||
}
|
}
|
||||||
GroupEntity groupEntity = em.find(GroupEntity.class, group.getId(), LockModeType.PESSIMISTIC_WRITE);
|
GroupEntity groupEntity = em.find(GroupEntity.class, group.getId(), LockModeType.PESSIMISTIC_WRITE);
|
||||||
if ((groupEntity == null) || (!groupEntity.getRealm().equals(realm.getId()))) {
|
if ((groupEntity == null) || (!groupEntity.getRealm().equals(realm.getId()))) {
|
||||||
|
@ -753,9 +754,9 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, RoleProv
|
||||||
if (Objects.isNull(groups)) return Collections.EMPTY_LIST;
|
if (Objects.isNull(groups)) return Collections.EMPTY_LIST;
|
||||||
List<GroupModel> list = new ArrayList<>();
|
List<GroupModel> list = new ArrayList<>();
|
||||||
for (String id : groups) {
|
for (String id : groups) {
|
||||||
GroupModel groupById = session.realms().getGroupById(id, realm);
|
GroupModel groupById = session.groups().getGroupById(realm, id);
|
||||||
while(Objects.nonNull(groupById.getParentId())) {
|
while(Objects.nonNull(groupById.getParentId())) {
|
||||||
groupById = session.realms().getGroupById(groupById.getParentId(), realm);
|
groupById = session.groups().getGroupById(realm, groupById.getParentId());
|
||||||
}
|
}
|
||||||
if(!list.contains(groupById)) {
|
if(!list.contains(groupById)) {
|
||||||
list.add(groupById);
|
list.add(groupById);
|
||||||
|
|
|
@ -797,7 +797,7 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
||||||
if (entities == null || entities.isEmpty()) return Collections.EMPTY_LIST;
|
if (entities == null || entities.isEmpty()) return Collections.EMPTY_LIST;
|
||||||
List<GroupModel> defaultGroups = new LinkedList<>();
|
List<GroupModel> defaultGroups = new LinkedList<>();
|
||||||
for (GroupEntity entity : entities) {
|
for (GroupEntity entity : entities) {
|
||||||
defaultGroups.add(session.realms().getGroupById(entity.getId(), this));
|
defaultGroups.add(session.groups().getGroupById(this, entity.getId()));
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(defaultGroups);
|
return Collections.unmodifiableList(defaultGroups);
|
||||||
}
|
}
|
||||||
|
@ -2022,52 +2022,52 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupModel createGroup(String id, String name, GroupModel toParent) {
|
public GroupModel createGroup(String id, String name, GroupModel toParent) {
|
||||||
return session.realms().createGroup(this, id, name, toParent);
|
return session.groups().createGroup(this, id, name, toParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveGroup(GroupModel group, GroupModel toParent) {
|
public void moveGroup(GroupModel group, GroupModel toParent) {
|
||||||
session.realms().moveGroup(this, group, toParent);
|
session.groups().moveGroup(this, group, toParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupModel getGroupById(String id) {
|
public GroupModel getGroupById(String id) {
|
||||||
return session.realms().getGroupById(id, this);
|
return session.groups().getGroupById(this, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GroupModel> getGroups() {
|
public List<GroupModel> getGroups() {
|
||||||
return session.realms().getGroups(this);
|
return session.groups().getGroups(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getGroupsCount(Boolean onlyTopGroups) {
|
public Long getGroupsCount(Boolean onlyTopGroups) {
|
||||||
return session.realms().getGroupsCount(this, onlyTopGroups);
|
return session.groups().getGroupsCount(this, onlyTopGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getGroupsCountByNameContaining(String search) {
|
public Long getGroupsCountByNameContaining(String search) {
|
||||||
return session.realms().getGroupsCountByNameContaining(this, search);
|
return session.groups().getGroupsCountByNameContaining(this, search);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GroupModel> getTopLevelGroups() {
|
public List<GroupModel> getTopLevelGroups() {
|
||||||
return session.realms().getTopLevelGroups(this);
|
return session.groups().getTopLevelGroups(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GroupModel> getTopLevelGroups(Integer first, Integer max) {
|
public List<GroupModel> getTopLevelGroups(Integer first, Integer max) {
|
||||||
return session.realms().getTopLevelGroups(this, first, max);
|
return session.groups().getTopLevelGroups(this, first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GroupModel> searchForGroupByName(String search, Integer first, Integer max) {
|
public List<GroupModel> searchForGroupByName(String search, Integer first, Integer max) {
|
||||||
return session.realms().searchForGroupByName(this, search, first, max);
|
return session.groups().searchForGroupByName(this, search, first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeGroup(GroupModel group) {
|
public boolean removeGroup(GroupModel group) {
|
||||||
return session.realms().removeGroup(this, group);
|
return session.groups().removeGroup(this, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -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.JpaGroupProviderFactory
|
|
@ -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 GroupProviderFactory extends ProviderFactory<GroupProvider> {
|
||||||
|
}
|
|
@ -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 GroupSpi implements Spi {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInternal() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "group";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Provider> getProviderClass() {
|
||||||
|
return GroupProvider.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||||
|
return GroupProviderFactory.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
package org.keycloak.models.cache;
|
package org.keycloak.models.cache;
|
||||||
|
|
||||||
import org.keycloak.models.ClientProvider;
|
import org.keycloak.models.ClientProvider;
|
||||||
|
import org.keycloak.models.GroupProvider;
|
||||||
import org.keycloak.models.RealmProvider;
|
import org.keycloak.models.RealmProvider;
|
||||||
import org.keycloak.models.RoleProvider;
|
import org.keycloak.models.RoleProvider;
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ import org.keycloak.models.RoleProvider;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface CacheRealmProvider extends RealmProvider, ClientProvider, RoleProvider {
|
public interface CacheRealmProvider extends RealmProvider, ClientProvider, GroupProvider, RoleProvider {
|
||||||
void clear();
|
void clear();
|
||||||
RealmProvider getRealmDelegate();
|
RealmProvider getRealmDelegate();
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ org.keycloak.provider.ExceptionConverterSpi
|
||||||
org.keycloak.storage.UserStorageProviderSpi
|
org.keycloak.storage.UserStorageProviderSpi
|
||||||
org.keycloak.storage.federated.UserFederatedStorageProviderSpi
|
org.keycloak.storage.federated.UserFederatedStorageProviderSpi
|
||||||
org.keycloak.models.ClientSpi
|
org.keycloak.models.ClientSpi
|
||||||
|
org.keycloak.models.GroupSpi
|
||||||
org.keycloak.models.RealmSpi
|
org.keycloak.models.RealmSpi
|
||||||
org.keycloak.models.RoleSpi
|
org.keycloak.models.RoleSpi
|
||||||
org.keycloak.models.ActionTokenStoreSpi
|
org.keycloak.models.ActionTokenStoreSpi
|
||||||
|
|
198
server-spi/src/main/java/org/keycloak/models/GroupProvider.java
Normal file
198
server-spi/src/main/java/org/keycloak/models/GroupProvider.java
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Provider of group records
|
||||||
|
* @author mhajas
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface GroupProvider extends Provider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a group from the given realm with the corresponding id
|
||||||
|
*
|
||||||
|
* @param id Id.
|
||||||
|
* @param realm Realm.
|
||||||
|
* @return GroupModel with the corresponding id.
|
||||||
|
* @deprecated Use method {@code getGroupById(realm, id)}
|
||||||
|
*/
|
||||||
|
default GroupModel getGroupById(String id, RealmModel realm) {
|
||||||
|
return getGroupById(realm, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a group from the given realm with the corresponding id
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param id Id.
|
||||||
|
* @return GroupModel with the corresponding id.
|
||||||
|
*/
|
||||||
|
GroupModel getGroupById(RealmModel realm, String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns groups for the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @return List of groups in the Realm.
|
||||||
|
*/
|
||||||
|
List<GroupModel> getGroups(RealmModel realm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a number of groups/top level groups (i.e. groups without parent group) for the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param onlyTopGroups When true the function returns a count of top level groups only.
|
||||||
|
* @return Number of groups/top level groups.
|
||||||
|
*/
|
||||||
|
Long getGroupsCount(RealmModel realm, Boolean onlyTopGroups);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns number of groups with the given string in name for the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param search Searched string.
|
||||||
|
* @return Number of groups with the given string in its name.
|
||||||
|
*/
|
||||||
|
Long getGroupsCountByNameContaining(RealmModel realm, String search);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns groups with the given role in the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param role Role.
|
||||||
|
* @param firstResult First result to return. Ignored if negative.
|
||||||
|
* @param maxResults Maximum number of results to return. Ignored if negative.
|
||||||
|
* @return List of groups with the given role.
|
||||||
|
*/
|
||||||
|
List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all top level groups (i.e. groups without parent group) for the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @return List of all top level groups in the realm.
|
||||||
|
*/
|
||||||
|
List<GroupModel> getTopLevelGroups(RealmModel realm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns top level groups (i.e. groups without parent group) for the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param firstResult First result to return.
|
||||||
|
* @param maxResults Maximum number of results to return.
|
||||||
|
* @return List of top level groups in the realm.
|
||||||
|
*/
|
||||||
|
List<GroupModel> getTopLevelGroups(RealmModel realm, Integer firstResult, Integer maxResults);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns groups with the given string in name for the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param search Searched string.
|
||||||
|
* @param firstResult First result to return. Ignored if {@code null}.
|
||||||
|
* @param maxResults Maximum number of results to return. Ignored if {@code null}.
|
||||||
|
* @return List of groups with the given string in name.
|
||||||
|
*/
|
||||||
|
List<GroupModel> searchForGroupByName(RealmModel realm, String search, Integer firstResult, Integer maxResults);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new group with the given name in the given realm.
|
||||||
|
* Effectively the same as {@code createGroup(realm, null, name, null)}.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param name Name.
|
||||||
|
* @return Model of the created group.
|
||||||
|
*/
|
||||||
|
default GroupModel createGroup(RealmModel realm, String name) {
|
||||||
|
return createGroup(realm, null, name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new group with the given id and name in the given realm.
|
||||||
|
* Effectively the same as {@code createGroup(realm, id, name, null)}
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param id Id.
|
||||||
|
* @param name Name.
|
||||||
|
* @return Model of the created group
|
||||||
|
*/
|
||||||
|
default GroupModel createGroup(RealmModel realm, String id, String name) {
|
||||||
|
return createGroup(realm, id, name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new group with the given name and parent to the given realm.
|
||||||
|
* Effectively the same as {@code createGroup(realm, null, name, toParent)}.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param name Name.
|
||||||
|
* @param toParent Parent group.
|
||||||
|
* @return Model of the created group.
|
||||||
|
*/
|
||||||
|
default GroupModel createGroup(RealmModel realm, String name, GroupModel toParent) {
|
||||||
|
return createGroup(realm, null, name, toParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new group with the given name, id, name and parent to the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param id Id, will be generated if {@code null}.
|
||||||
|
* @param name Name.
|
||||||
|
* @param toParent Parent group, or {@code null} if the group is top level group
|
||||||
|
* @return Model of the created group
|
||||||
|
*/
|
||||||
|
GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given group for the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param group Group.
|
||||||
|
* @return true if the group was removed, false if group doesn't exist or doesn't belong to the given realm
|
||||||
|
*/
|
||||||
|
boolean removeGroup(RealmModel realm, GroupModel group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used for moving groups in group structure, for example:
|
||||||
|
* <ul>
|
||||||
|
* <li>making an existing child group child group of some other group,</li>
|
||||||
|
* <li>setting a top level group (i.e. group without parent group) child of some group,</li>
|
||||||
|
* <li>making a child group top level group (i.e. removing its parent group).</li>
|
||||||
|
* <ul/>
|
||||||
|
*
|
||||||
|
* @param realm Realm owning this group.
|
||||||
|
* @param group Group to update.
|
||||||
|
* @param toParent New parent group, or {@code null} if we are moving the group to top level group.
|
||||||
|
*/
|
||||||
|
void moveGroup(RealmModel realm, GroupModel group, GroupModel toParent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes parent group for the given group in the given realm.
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param subGroup Group.
|
||||||
|
*/
|
||||||
|
void addTopLevelGroup(RealmModel realm, GroupModel subGroup);
|
||||||
|
}
|
|
@ -115,6 +115,14 @@ public interface KeycloakSession {
|
||||||
*/
|
*/
|
||||||
ClientProvider clients();
|
ClientProvider clients();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a managed group provider instance.
|
||||||
|
*
|
||||||
|
* @return Currently used GroupProvider instance.
|
||||||
|
* @throws IllegalStateException if transaction is not active
|
||||||
|
*/
|
||||||
|
GroupProvider groups();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a managed provider instance. Will start a provider transaction. This transaction is managed by the KeycloakSession
|
* Returns a managed provider instance. Will start a provider transaction. This transaction is managed by the KeycloakSession
|
||||||
* transaction.
|
* transaction.
|
||||||
|
@ -192,6 +200,13 @@ public interface KeycloakSession {
|
||||||
*/
|
*/
|
||||||
ClientProvider clientLocalStorage();
|
ClientProvider clientLocalStorage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keycloak specific local storage for groups. No cache in front, this api talks directly to storage configured for Keycloak
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
GroupProvider groupLocalStorage();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keycloak specific local storage for roles. No cache in front, this api talks directly to storage configured for Keycloak
|
* Keycloak specific local storage for roles. No cache in front, this api talks directly to storage configured for Keycloak
|
||||||
*
|
*
|
||||||
|
|
|
@ -28,7 +28,7 @@ import java.util.stream.Collectors;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface RealmProvider extends Provider /* TODO: Remove in future version */, ClientProvider, RoleProvider /* up to here */ {
|
public interface RealmProvider extends Provider /* TODO: Remove in future version */, ClientProvider, GroupProvider, 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
|
// Note: The reason there are so many query methods here is for layering a cache on top of an persistent KeycloakSession
|
||||||
MigrationModel getMigrationModel();
|
MigrationModel getMigrationModel();
|
||||||
|
@ -37,42 +37,7 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
|
||||||
RealmModel getRealm(String id);
|
RealmModel getRealm(String id);
|
||||||
RealmModel getRealmByName(String name);
|
RealmModel getRealmByName(String name);
|
||||||
|
|
||||||
void moveGroup(RealmModel realm, GroupModel group, GroupModel toParent);
|
|
||||||
|
|
||||||
List<GroupModel> getGroups(RealmModel realm);
|
|
||||||
|
|
||||||
Long getGroupsCount(RealmModel realm, Boolean onlyTopGroups);
|
|
||||||
|
|
||||||
Long getGroupsCountByNameContaining(RealmModel realm, String search);
|
|
||||||
|
|
||||||
List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults);
|
|
||||||
|
|
||||||
List<GroupModel> getTopLevelGroups(RealmModel realm);
|
|
||||||
|
|
||||||
List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max);
|
|
||||||
|
|
||||||
List searchForGroupByName(RealmModel realm, String search, Integer first, Integer max);
|
|
||||||
|
|
||||||
boolean removeGroup(RealmModel realm, GroupModel group);
|
|
||||||
|
|
||||||
default GroupModel createGroup(RealmModel realm, String name) {
|
|
||||||
return createGroup(realm, null, name, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
default GroupModel createGroup(RealmModel realm, String id, String name) {
|
|
||||||
return createGroup(realm, id, name, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
default GroupModel createGroup(RealmModel realm, String name, GroupModel toParent) {
|
|
||||||
return createGroup(realm, null, name, toParent);
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent);
|
|
||||||
|
|
||||||
void addTopLevelGroup(RealmModel realm, GroupModel subGroup);
|
|
||||||
|
|
||||||
ClientScopeModel getClientScopeById(String id, RealmModel realm);
|
ClientScopeModel getClientScopeById(String id, RealmModel realm);
|
||||||
GroupModel getGroupById(String id, RealmModel realm);
|
|
||||||
|
|
||||||
List<RealmModel> getRealms();
|
List<RealmModel> getRealms();
|
||||||
List<RealmModel> getRealmsWithProviderType(Class<?> type);
|
List<RealmModel> getRealmsWithProviderType(Class<?> type);
|
||||||
|
@ -232,4 +197,93 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
|
||||||
return searchForClientRolesStream(client, search, first, max).collect(Collectors.toSet());
|
return searchForClientRolesStream(client, search, first, max).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* GROUP PROVIDER METHODS */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
void moveGroup(RealmModel realm, GroupModel group, GroupModel toParent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
GroupModel getGroupById(RealmModel realm, String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
default GroupModel getGroupById(String id, RealmModel realm) {
|
||||||
|
return getGroupById(realm, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
List<GroupModel> getGroups(RealmModel realm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
Long getGroupsCount(RealmModel realm, Boolean onlyTopGroups);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
Long getGroupsCountByNameContaining(RealmModel realm, String search);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
List<GroupModel> getGroupsByRole(RealmModel realm, RoleModel role, int firstResult, int maxResults);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
List<GroupModel> getTopLevelGroups(RealmModel realm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
List<GroupModel> getTopLevelGroups(RealmModel realm, Integer first, Integer max);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
List searchForGroupByName(RealmModel realm, String search, Integer first, Integer max);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
boolean removeGroup(RealmModel realm, GroupModel group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
default GroupModel createGroup(RealmModel realm, String name) {
|
||||||
|
return createGroup(realm, null, name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
default GroupModel createGroup(RealmModel realm, String id, String name) {
|
||||||
|
return createGroup(realm, id, name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
default GroupModel createGroup(RealmModel realm, String name, GroupModel toParent) {
|
||||||
|
return createGroup(realm, null, name, toParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use the corresponding method from {@link GroupProvider}. */
|
||||||
|
@Override
|
||||||
|
void addTopLevelGroup(RealmModel realm, GroupModel subGroup);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.keycloak.credential.UserCredentialStoreManager;
|
||||||
import org.keycloak.jose.jws.DefaultTokenManager;
|
import org.keycloak.jose.jws.DefaultTokenManager;
|
||||||
import org.keycloak.keys.DefaultKeyManager;
|
import org.keycloak.keys.DefaultKeyManager;
|
||||||
import org.keycloak.models.ClientProvider;
|
import org.keycloak.models.ClientProvider;
|
||||||
|
import org.keycloak.models.GroupProvider;
|
||||||
import org.keycloak.models.TokenManager;
|
import org.keycloak.models.TokenManager;
|
||||||
import org.keycloak.models.KeycloakContext;
|
import org.keycloak.models.KeycloakContext;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -69,6 +70,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
private final Map<String, Object> attributes = new HashMap<>();
|
private final Map<String, Object> attributes = new HashMap<>();
|
||||||
private RealmProvider model;
|
private RealmProvider model;
|
||||||
private ClientProvider clientProvider;
|
private ClientProvider clientProvider;
|
||||||
|
private GroupProvider groupProvider;
|
||||||
private RoleProvider roleProvider;
|
private RoleProvider roleProvider;
|
||||||
private UserStorageManager userStorageManager;
|
private UserStorageManager userStorageManager;
|
||||||
private ClientStorageManager clientStorageManager;
|
private ClientStorageManager clientStorageManager;
|
||||||
|
@ -114,6 +116,16 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private GroupProvider getGroupProvider() {
|
||||||
|
// TODO: Extract GroupProvider from CacheRealmProvider and use that instead
|
||||||
|
GroupProvider cache = getProvider(CacheRealmProvider.class);
|
||||||
|
if (cache != null) {
|
||||||
|
return cache;
|
||||||
|
} else {
|
||||||
|
return groupLocalStorage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private RoleProvider getRoleProvider() {
|
private RoleProvider getRoleProvider() {
|
||||||
// TODO: Extract RoleProvider from CacheRealmProvider and use that instead
|
// TODO: Extract RoleProvider from CacheRealmProvider and use that instead
|
||||||
RoleProvider cache = getProvider(CacheRealmProvider.class);
|
RoleProvider cache = getProvider(CacheRealmProvider.class);
|
||||||
|
@ -190,6 +202,11 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
return getProvider(ClientProvider.class);
|
return getProvider(ClientProvider.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupProvider groupLocalStorage() {
|
||||||
|
return getProvider(GroupProvider.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientProvider clientStorageManager() {
|
public ClientProvider clientStorageManager() {
|
||||||
if (clientStorageManager == null) {
|
if (clientStorageManager == null) {
|
||||||
|
@ -323,6 +340,14 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
return clientProvider;
|
return clientProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupProvider groups() {
|
||||||
|
if (groupProvider == null) {
|
||||||
|
groupProvider = getGroupProvider();
|
||||||
|
}
|
||||||
|
return groupProvider;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RoleProvider roles() {
|
public RoleProvider roles() {
|
||||||
if (roleProvider == null) {
|
if (roleProvider == null) {
|
||||||
|
|
|
@ -455,7 +455,7 @@ public class RoleContainerResource extends RoleResource {
|
||||||
throw new NotFoundException("Could not find role");
|
throw new NotFoundException("Could not find role");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GroupModel> groupsModel = session.realms().getGroupsByRole(realm, role, firstResult, maxResults);
|
List<GroupModel> groupsModel = session.groups().getGroupsByRole(realm, role, firstResult, maxResults);
|
||||||
|
|
||||||
return groupsModel.stream()
|
return groupsModel.stream()
|
||||||
.map(g -> ModelToRepresentation.toRepresentation(g, !briefRepresentation))
|
.map(g -> ModelToRepresentation.toRepresentation(g, !briefRepresentation))
|
||||||
|
|
|
@ -79,7 +79,6 @@ import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.DefaultValue;
|
import javax.ws.rs.DefaultValue;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.NotSupportedException;
|
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
@ -911,7 +910,7 @@ public class UserResource {
|
||||||
public void removeMembership(@PathParam("groupId") String groupId) {
|
public void removeMembership(@PathParam("groupId") String groupId) {
|
||||||
auth.users().requireManageGroupMembership(user);
|
auth.users().requireManageGroupMembership(user);
|
||||||
|
|
||||||
GroupModel group = session.realms().getGroupById(groupId, realm);
|
GroupModel group = session.groups().getGroupById(realm, groupId);
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
throw new NotFoundException("Group not found");
|
throw new NotFoundException("Group not found");
|
||||||
}
|
}
|
||||||
|
@ -934,7 +933,7 @@ public class UserResource {
|
||||||
@NoCache
|
@NoCache
|
||||||
public void joinGroup(@PathParam("groupId") String groupId) {
|
public void joinGroup(@PathParam("groupId") String groupId) {
|
||||||
auth.users().requireManageGroupMembership(user);
|
auth.users().requireManageGroupMembership(user);
|
||||||
GroupModel group = session.realms().getGroupById(groupId, realm);
|
GroupModel group = session.groups().getGroupById(realm, groupId);
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
throw new NotFoundException("Group not found");
|
throw new NotFoundException("Group not found");
|
||||||
}
|
}
|
||||||
|
|
|
@ -894,7 +894,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
session.getContext().setRealm(realm);
|
session.getContext().setRealm(realm);
|
||||||
|
|
||||||
GroupModel customerAGroup = session.realms().createGroup(realm, "Customer A");
|
GroupModel customerAGroup = session.groups().createGroup(realm, "Customer A");
|
||||||
UserModel customerAManager = session.users().addUser(realm, "customer-a-manager");
|
UserModel customerAManager = session.users().addUser(realm, "customer-a-manager");
|
||||||
session.userCredentialManager().updateCredential(realm, customerAManager, UserCredentialModel.password("password"));
|
session.userCredentialManager().updateCredential(realm, customerAManager, UserCredentialModel.password("password"));
|
||||||
ClientModel realmAdminClient = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
ClientModel realmAdminClient = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
||||||
|
|
|
@ -48,6 +48,10 @@
|
||||||
"provider": "${keycloak.client.provider:jpa}"
|
"provider": "${keycloak.client.provider:jpa}"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"group": {
|
||||||
|
"provider": "${keycloak.group.provider:jpa}"
|
||||||
|
},
|
||||||
|
|
||||||
"role": {
|
"role": {
|
||||||
"provider": "${keycloak.role.provider:jpa}"
|
"provider": "${keycloak.role.provider:jpa}"
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
"provider": "${keycloak.client.provider:jpa}"
|
"provider": "${keycloak.client.provider:jpa}"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"group": {
|
||||||
|
"provider": "${keycloak.group.provider:jpa}"
|
||||||
|
},
|
||||||
|
|
||||||
"role": {
|
"role": {
|
||||||
"provider": "${keycloak.role.provider:jpa}"
|
"provider": "${keycloak.role.provider:jpa}"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue