8000 Filtering organization groups when managing or processing groups by pedroigor · Pull Request #30590 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -290,4 +290,10 @@ private GroupModel getGroupModel() {
public boolean escapeSlashesInGroupPath() {
return KeycloakModelUtils.escapeSlashesInGroupPath(keycloakSession);
}

@Override
public Type getType() {
if (isUpdated()) return updated.getType();
return cached.getType();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ClientScopeProvider;
import org.keycloak.models.GroupModel;
import org.keycloak.models.GroupModel.Type;
import org.keycloak.models.GroupProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
Expand Down Expand Up @@ -1170,8 +1171,8 @@ private GroupModel groupAdded(RealmModel realm, GroupModel group, GroupModel toP
}

@Override
public GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent) {
GroupModel group = getGroupDelegate().createGroup(realm, id, name, toParent);
public GroupModel createGroup(RealmModel realm, String id, Type type, String name, GroupModel toParent) {
GroupModel group = getGroupDelegate().createGroup(realm, id, type, name, toParent);
return groupAdded(realm, group, toParent);
}

Expand Down Expand Up @@ -1603,4 +1604,10 @@ public String getLocalizationTextsById(RealmModel realm, String locale, String k
}
return null;
}

@Override
public void preRemove(RealmModel realm) {
listInvalidations.add(realm.getId());
getGroupDelegate().preRemove(realm);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.keycloak.credential.CredentialModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.GroupModel.Type;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
Expand All @@ -31,8 +32,8 @@
import org.keycloak.models.utils.RoleUtils;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -395,19 +396,33 @@ public void deleteRoleMapping(RoleModel role) {

@Override
public Stream<GroupModel> getGroupsStream() {
if (updated != null) return updated.getGroupsStream();
Set<GroupModel> groups = new LinkedHashSet<>();
for (String id : cached.getGroups(modelSupplier)) {
GroupModel groupModel = keycloakSession.groups().getGroupById(realm, id);
if (groupModel == null) {
// chance that role was removed, so just delete to persistence and get user invalidated
getDelegateForUpdate();
return updated.getGroupsStream();
Stream<GroupModel> result = Stream.empty();

if (updated != null) {
result = updated.getGroupsStream();
} else {
Set<GroupModel> groups = null;
for (String id : cached.getGroups(modelSupplier)) {
GroupModel groupModel = keycloakSession.groups().getGroupById(realm, id);
if (groupModel == null) {
// chance that role was removed, so just delegate to persistence and get user invalidated
getDelegateForUpdate();
result = updated.getGroupsStream();
break;
} else {
if (groups == null) {
groups = new HashSet<>();
}
groups.add(groupModel);
}
}
groups.add(groupModel);

if (groups != null) {
result = groups.stream();
}
}
return groups.stream();

return result.filter(g -> Type.REALM.equals(g.getType())).sorted(Comparator.comparing(GroupModel::getName));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.models.GroupModel;
import org.keycloak.models.GroupModel.Type;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.cache.infinispan.DefaultLazyLoader;
Expand All @@ -42,6 +43,7 @@ public class CachedGroup extends AbstractRevisioned implements InRealm {
private final LazyLoader<GroupModel, Set<String>> roleMappings;
private final LazyLoader<GroupModel, Set<String>> subGroups;
private final LazyLoader<GroupModel, Long> subGroupsCount;
private final Type type;

public CachedGroup(Long revision, RealmModel realm, GroupModel group) {
super(revision, group.getId());
Expand All @@ -52,6 +54,7 @@ public CachedGroup(Long revision, RealmModel realm, GroupModel group) {
this.roleMappings = new DefaultLazyLoader<>(source -> source.getRoleMappingsStream().map(RoleModel::getId).collect(Collectors.toSet()), Collections::emptySet);
this.subGroups = new DefaultLazyLoader<>(source -> source.getSubGroupsStream().map(GroupModel::getId).collect(Collectors.toSet()), Collections::emptySet);
this.subGroupsCount = new DefaultLazyLoader<>(GroupModel::getSubGroupsCount, () -> 0L);
this.type = group.getType();
}

public String getRealm() {
Expand Down Expand Up @@ -85,4 +88,8 @@ public Set<String> getSubGroups(Supplier<GroupModel> group) {
public Long getSubGroupsCount(Supplier<GroupModel> group) {
return subGroupsCount.get(group);
}

public Type getType() {
return type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2024 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.connections.jpa.updater.liquibase.custom;

import liquibase.exception.CustomChangeException;
import liquibase.statement.core.RawSqlStatement;

public class JpaUpdate26_0_0_OrganizationGroupType extends CustomKeycloakTask {

@Override
protected void generateStatementsImpl() throws CustomChangeException {
String groupTable = getTableName("KEYCLOAK_GROUP");
String orgTable = getTableName("ORG");

if ("mariadb".equals(database.getShortName())) {
statements.add(new RawSqlStatement("UPDATE " + groupTable + " SET TYPE = 1 WHERE CONVERT(NAME USING utf8) IN (SELECT CONVERT(ID USING utf8) FROM " + orgTable + ")"));
} else {
statements.add(new RawSqlStatement("UPDATE " + groupTable + " SET TYPE = 1 WHERE NAME IN (SELECT ID FROM " + orgTable + ")"));
}
}

@Override
protected String getTaskId() {
return "Update type and id for organization groups";
}

}


Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,11 @@ public Stream<RoleModel> getClientRoleMappingsStream(ClientModel app) {
return getRoleMappingsStream().filter(r -> RoleUtils.isClientRole(r, app));
}

@Override
public Type getType() {
return Type.valueOf(group.getType());
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import org.keycloak.models.GroupModel.GroupCreatedEvent;
import org.keycloak.models.GroupModel.GroupPathChangeEvent;
import org.keycloak.models.GroupModel.GroupUpdatedEvent;
import org.keycloak.models.GroupModel.Type;
import org.keycloak.models.GroupProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
Expand All @@ -78,7 +79,6 @@
import org.keycloak.models.jpa.entities.RealmLocalizationTextsEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.organization.utils.Organizations;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;


Expand Down Expand Up @@ -180,20 +180,21 @@ public boolean removeRealm(String id) {
realm.getDefaultGroupIds().clear();
em.flush();

int num = em.createNamedQuery("deleteGroupRoleMappingsByRealm")
.setParameter("realm", realm.getId()).executeUpdate();

session.clients().removeClients(adapter);

num = em.createNamedQuery("deleteDefaultClientScopeRealmMappingByRealm")
em.createNamedQuery("deleteDefaultClientScopeRealmMappingByRealm")
.setParameter("realm", realm).executeUpdate();

session.clientScopes().removeClientScopes(adapter);
session.roles().removeRoles(adapter);

session.groups().getTopLevelGroupsStream(adapter).forEach(Organizations.removeGroup(session, adapter));
em.createNamedQuery("deleteOrganizationDomainsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
em.createNamedQuery("deleteOrganizationsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
10BC8
Comment thread
pedroigor marked this conversation as resolved.
Resolved
session.groups().preRemove(adapter);

num = em.createNamedQuery("removeClientInitialAccessByRealm")
em.createNamedQuery("removeClientInitialAccessByRealm")
.setParameter("realm", realm).executeUpdate();

em.remove(realm);
Expand Down Expand Up @@ -701,7 +702,7 @@ public KeycloakSession getKeycloakSession() {
}

@Override
public GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent) {
public GroupModel createGroup(RealmModel realm, String id, Type type, String name, GroupModel toParent) {
if (id == null) {
id = KeycloakModelUtils.generateId();
} else if (GroupEntity.TOP_PARENT_ID.equals(id)) {
Expand All @@ -713,6 +714,7 @@ public GroupModel createGroup(RealmModel realm, String id, String name, GroupMod
groupEntity.setName(name);
groupEntity.setRealm(realm.getId());
groupEntity.setParentId(toParent == null? GroupEntity.TOP_PARENT_ID : toParent.getId());
groupEntity.setType(type == null ? Type.REALM.intValue() : type.intValue());
em.persist(groupEntity);
em.flush();

Expand All @@ -739,6 +741,16 @@ public void preRemove(RealmModel realm, RoleModel role) {
em.createNativeQuery("delete from " + clientScopeMapping + " where ROLE_ID = :role").setParameter("role", role.getId()).executeUpdate();
}

@Override
public void preRemove(RealmModel realm) {
em.createNamedQuery("deleteGroupRoleMappingsByRealm")
.setParameter("realm", realm.getId()).executeUpdate();
em.createNamedQuery("deleteGroupAttributesByRealm")
.setParameter("realm", realm.getId()).executeUpdate();
em.createNamedQuery("deleteGroupsByRealm")
.setParameter("realm", realm.getId()).executeUpdate();
}

@Override
public ClientModel addClient(RealmModel realm, String clientId) {
return addClient(realm, KeycloakModelUtils.generateId(), clientId);
Expand Down Expand Up @@ -1149,6 +1161,7 @@ public Stream<GroupModel> searchGroupsByAttributes(RealmModel realm, Map<String,
List<Predicate> predicates = new ArrayList<>();

predicates.add(builder.equal(root.get("realm"), realm.getId()));
predicates.add(builder.equal(root.get("type"), Type.REALM.intValue()));

for (Map.Entry<String, String> entry : filteredAttributes.entrySet()) {
String key = entry.getKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package org.keycloak.models.jpa;

import org.keycloak.common.Profile;
import org.keycloak.common.Profile.Feature;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.ObjectUtil;
import org.keycloak.credential.UserCredentialManager;
Expand Down Expand Up @@ -45,6 +47,8 @@
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.keycloak.organization.OrganizationProvider;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -391,7 +395,15 @@ public Stream<GroupModel> getGroupsStream(String search, Integer first, Integer

@Override
public long getGroupsCount() {
return createCountGroupsQuery().getSingleResult();
Long result = createCountGroupsQuery().getSingleResult();
if (Profile.isFeatureEnabled(Feature.ORGANIZATION)) {
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
if (result > 0 && provider.getByMember(this) != null) {
// remove from the count the organization group membership
result--;
}
}
return result;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
*/
@NamedQueries({
@NamedQuery(name="getGroupAttributesByNameAndValue", query="select attr from GroupAttributeEntity attr where attr.name = :name and attr.value = :value"),
@NamedQuery(name="deleteGroupAttributesByRealm", query="delete from GroupAttributeEntity a where a.group IN (select u from GroupEntity u where u.realm=:realm)")
})
@Table(name="GROUP_ATTRIBUTE")
@Entity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@
* @version $Revision: 1 $
*/
@NamedQueries({
@NamedQuery(name="getGroupIdsByParent", query="select u.id from GroupEntity u where u.realm = :realm and u.parentId = :parent order by u.name ASC"),
@NamedQuery(name="getGroupIdsByParentAndName", query="select u.id from GroupEntity u where u.realm = :realm and u.parentId = :parent and u.name = :search order by u.name ASC"),
@NamedQuery(name="getGroupIdsByParentAndNameContaining", query="select u.id from GroupEntity u where u.realm = :realm and u.parentId = :parent and lower(u.name) like lower(concat('%',:search,'%')) order by u.name ASC"),
@NamedQuery(name="getGroupIdsByRealm", query="select u.id from GroupEntity u where u.realm = :realm order by u.name ASC"),
@NamedQuery(name="getGroupIdsByNameContaining", query="select u.id from GroupEntity u where u.realm = :realm and lower(u.name) like lower(concat('%',:search,'%')) order by u.name ASC"),
@NamedQuery(name="getGroupIdsByNameContainingFromIdList", query="select u.id from GroupEntity u where u.realm = :realm and lower(u.name) like lower(concat('%',:search,'%')) and u.id in :ids order by u.name ASC"),
@NamedQuery(name="getGroupIdsByName", query="select u.id from GroupEntity u where u.realm = :realm and u.name = :search order by u.name ASC"),
@NamedQuery(name="getGroupIdsFromIdList", query="select u.id from GroupEntity u where u.realm = :realm and u.id in :ids order by u.name ASC"),
@NamedQuery(name="getGroupCountByNameContainingFromIdList", query="select count(u) from GroupEntity u where u.realm = :realm and lower(u.name) like lower(concat('%',:search,'%')) and u.id in :ids"),
@NamedQuery(name="getGroupCount", query="select count(u) from GroupEntity u where u.realm = :realm"),
@NamedQuery(name="getGroupCountByParent", query="select count(u) from GroupEntity u where u.realm = :realm and u.parentId = :parent")
@NamedQuery(name="getGroupIdsByParent", query="select u.id from GroupEntity u where u.realm = :realm and u.type = 0 and u.parentId = :parent order by u.name ASC"),
@NamedQuery(name="getGroupIdsByParentAndName", query="select u.id from GroupEntity u where u.realm = :realm and u.type = 0 and u.parentId = :parent and u.name = :search order by u.name ASC"),
@NamedQuery(name="getGroupIdsByParentAndNameContaining", query="select u.id from GroupEntity u where u.realm = :realm and u.type = 0 and u.parentId = :parent and lower(u.name) like lower(concat('%',:search,'%')) order by u.name ASC"),
@NamedQuery(name="getGroupIdsByRealm", query="select u.id from GroupEntity u where u.realm = :realm and u.type = 0 order by u.name ASC"),
@NamedQuery(name="getGroupIdsByNameContaining", query="select u.id from GroupEntity u where u.realm = :realm and u.type = 0 and lower(u.name) like lower(concat('%',:search,'%')) order by u.name ASC"),
@NamedQuery(name="getGroupIdsByNameContainingFromIdList", query="select u.id from GroupEntity u where u.realm = :realm and u.type = 0 and lower(u.name) like lower(concat('%',:search,'%')) and u.id in :ids order by u.name ASC"),
@NamedQuery(name="getGroupIdsByName", query="select u.id from GroupEntity u where u.realm = :realm and u.type = 0 and u.name = :search order by u.name ASC"),
@NamedQuery(name="getGroupIdsFromIdList", query="select u.id from GroupEntity u where u.realm = :realm and u.type = 0 and u.id in :ids order by u.name ASC"),
@NamedQuery(name="getGroupCountByNameContainingFromIdList", query="select count(u) from GroupEntity u where u.realm = :realm and u.type = 0 and lower(u.name) like lower(concat('%',:search,'%')) and u.id in :ids"),
@NamedQuery(name="getGroupCount", query="select count(u) from GroupEntity u where u.realm = :realm and u.type = 0"),
@NamedQuery(name="getGroupCountByParent", query="select count(u) from GroupEntity u where u.realm = :realm and u.type = 0 and u.parentId = :parent"),
@NamedQuery(name="deleteGroupsByRealm", query="delete from GroupEntity g where g.realm = :realm")
})
@Entity
@Table(name="KEYCLOAK_GROUP",
Expand Down Expand Up @@ -66,6 +67,9 @@ public class GroupEntity {
@Column(name = "REALM_ID")
private String realm;

@Column(name = "TYPE")
private int type;

@OneToMany(
cascade = CascadeType.REMOVE,
orphanRemoval = true, mappedBy="group")
Expand Down Expand Up @@ -114,6 +118,14 @@ public void setParentId(String parentId) {
this.parentId = parentId;
}

public int getType() {
return type;
}

public void setType(int type) {
this.type = type;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;

/**
Expand All @@ -34,6 +36,9 @@
*/
@Entity
@Table(name="ORG_DOMAIN")
@NamedQueries({
@NamedQuery(name="deleteOrganizationDomainsB 581E yRealm", query="delete from OrganizationDomainEntity d where d.organization IN (select o from OrganizationEntity o where o.realmId=:realmId)")
})
public class OrganizationDomainEntity {

@Id
Expand Down
Loading
0