mirror of
https://github.com/keycloak/keycloak.git
synced 2026-02-18 18:37:54 -05:00
fix: adding admin role invalidation when a new realm is found (#46019)
* fix: adding admin role invalidation when a new realm is found closes: #45966 Signed-off-by: Steve Hawkins <shawkins@redhat.com> * Update model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java Co-authored-by: Alexander Schwartz <alexander.schwartz@gmx.net> Signed-off-by: Steven Hawkins <shawkins@redhat.com> * adding a comment and a permission tweak for imported realms Signed-off-by: Steve Hawkins <shawkins@redhat.com> * checking getShouldUseLightweightToken Signed-off-by: Steve Hawkins <shawkins@redhat.com> --------- Signed-off-by: Steve Hawkins <shawkins@redhat.com> Signed-off-by: Steven Hawkins <shawkins@redhat.com> Co-authored-by: Alexander Schwartz <alexander.schwartz@gmx.net>
This commit is contained in:
parent
74988b5c0a
commit
19118a097c
3 changed files with 81 additions and 1 deletions
|
|
@ -26,10 +26,12 @@ import java.util.Set;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.client.clienttype.ClientTypeManager;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.ClientInitialAccessModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientProvider;
|
||||
|
|
@ -490,6 +492,21 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// to accommodate imports of new realms, check to see if the master admin role is up-to-date
|
||||
if (!model.getName().equals(Config.getAdminRealm())) {
|
||||
RealmModel adminRealm = session.realms().getRealmByName(Config.getAdminRealm());
|
||||
if (adminRealm != null) {
|
||||
ClientModel clientModel = session.clients().getClientByClientId(adminRealm, model.getName() + "-realm");
|
||||
if (clientModel != null) {
|
||||
RoleModel adminRole = adminRealm.getRole(AdminRoles.ADMIN);
|
||||
if (adminRole.getCompositesStream().noneMatch(r -> (r.isClientRole() && r.getContainerId().equals(clientModel.getId())))) {
|
||||
registerRoleInvalidation(adminRole.getId(), adminRole.getName(), adminRole.getContainerId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cached = new CachedRealm(loaded, model);
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
adapter = new RealmAdapter(session, cached, this);
|
||||
|
|
@ -1057,6 +1074,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
return list.stream().sorted(GroupModel.COMPARE_BY_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<GroupModel> getGroupsStream(RealmModel realm, Stream<String> ids, String search, Integer first, Integer max) {
|
||||
return getGroupDelegate().getGroupsStream(realm, ids, search, first, max);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,15 @@
|
|||
package org.keycloak.it.cli.dist;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.keycloak.it.junit5.extension.CLIResult;
|
||||
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
import org.keycloak.it.utils.RawKeycloakDistribution;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
|
@ -32,6 +35,9 @@ import org.junit.jupiter.api.Tag;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@DistributionTest(defaultOptions = "--db=dev-file")
|
||||
@RawDistOnly(reason = "Containers are immutable")
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
|
|
@ -69,4 +75,37 @@ public class ImportDistTest {
|
|||
cliResult = dist.run("import");
|
||||
cliResult.assertError("Must specify either --dir or --file options.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testImportNewRealm(KeycloakDistribution dist) throws IOException {
|
||||
File file = new File("target/realm.json");
|
||||
|
||||
RealmRepresentation newRealm=new RealmRepresentation();
|
||||
newRealm.setRealm("anotherRealm");
|
||||
newRealm.setId("anotherRealm");
|
||||
newRealm.setEnabled(true);
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
mapper.writeValue(fos, newRealm);
|
||||
}
|
||||
|
||||
var cliResult = dist.run("import", "--file=" + file.getAbsolutePath());
|
||||
cliResult.assertMessage("Realm 'anotherRealm' imported");
|
||||
|
||||
dist.setEnvVar("MY_SECRET", "admin123");
|
||||
|
||||
RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class);
|
||||
CLIResult result = rawDist.run("bootstrap-admin", "service", "--db=dev-file", "--client-id=admin", "--client-secret:env=MY_SECRET");
|
||||
|
||||
assertTrue(result.getErrorOutput().isEmpty(), result.getErrorOutput());
|
||||
|
||||
rawDist.setManualStop(true);
|
||||
rawDist.run("start-dev");
|
||||
|
||||
CLIResult adminResult = rawDist.kcadm("get", "realms", "--server", "http://localhost:8080", "--realm", "master", "--client", "admin", "--secret", "admin123");
|
||||
|
||||
assertEquals(0, adminResult.exitCode());
|
||||
assertTrue(adminResult.getOutput().contains("anotherRealm"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.keycloak.services.resources.admin.fgap;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.ws.rs.ForbiddenException;
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
|
|
@ -165,16 +167,37 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage
|
|||
public boolean hasOneAdminRole(RealmModel realm, String... adminRoles) {
|
||||
String clientId;
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
boolean masterAdminRealm = false;
|
||||
if (RealmManager.isAdministrationRealm(adminsRealm)) {
|
||||
clientId = realm.getMasterAdminClient().getClientId();
|
||||
masterAdminRealm = true;
|
||||
} else if (adminsRealm.equals(realm)) {
|
||||
clientId = realm.getClientByClientId(realmManager.getRealmAdminClientId(realm)).getClientId();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return identity.hasOneClientRole(clientId, adminRoles);
|
||||
boolean result = identity.hasOneClientRole(clientId, adminRoles);
|
||||
if (!result && masterAdminRealm && !adminsRealm.equals(realm)
|
||||
&& AbstractOIDCProtocolMapper.getShouldUseLightweightToken(session)
|
||||
&& hasNewAdminRoles(realm, clientId, adminRoles)) {
|
||||
return true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean hasNewAdminRoles(RealmModel realm, String clientId, String... adminRoles) {
|
||||
RealmModel masterRealm = getMasterRealm();
|
||||
UserModel admin = admin();
|
||||
RoleModel masterAdminRole = masterRealm.getRole(AdminRoles.ADMIN);
|
||||
if (!admin.hasRole(masterAdminRole)) {
|
||||
return false;
|
||||
}
|
||||
Set<String> roleNames = Set.of(adminRoles);
|
||||
ClientModel clientModel = masterRealm.getClientByClientId(clientId);
|
||||
return clientModel != null && masterAdminRole.getCompositesStream()
|
||||
.anyMatch(r -> (r.isClientRole() && r.getContainerId().equals(clientModel.getId())
|
||||
&& roleNames.contains(r.getName())));
|
||||
}
|
||||
|
||||
public boolean isAdminSameRealm() {
|
||||
return auth == null || realm.getId().equals(auth.getRealm().getId());
|
||||
|
|
|
|||
Loading…
Reference in a new issue