mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Expose system-info information in the serverinfo endpoint only for users in the admin realm
Closes #42828 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
cfec364b17
commit
1d28c0cd35
@ -243,6 +243,17 @@ with the LDAP server.
|
||||
This change improves the resilience of the system when there are temporary issues with the LDAP server, ensuring that local users can still be accessed even if the LDAP search fails.
|
||||
If a local user is linked to a failing LDAP provider, the user will be marked as disabled and read-only until the LDAP server is available again.
|
||||
|
||||
=== The `serverinfo` endpoint only returns the system info for administrators in the administrator realm
|
||||
|
||||
Starting with this version, the `serverinfo` endpoint, which is used by the admin console to obtain some general information of the {project_name} installation, will only return the system information for administrators in the administration (master) realm. This change was done for security reasons.
|
||||
|
||||
If, for whatever reason, an administrator in a common realm needs to access the `systemInfo`, `cpuInfo` or `memoryInfo` fields of the `serverinfo` response, you need to create and assign a new *view-system* role to that admin user:
|
||||
|
||||
. In the affected realm, select the management client *realm-management*, and, in the *Roles* tab, create a new role called *view-system*.
|
||||
. In *Users* select the administrator account, and, in the *Role mapping* tab, assign the just created *view-system* client role to the admin user.
|
||||
|
||||
The previous workaround is marked as deprecated and it can be removed in a future version of {project_name}.
|
||||
|
||||
// ------------------------ Deprecated features ------------------------ //
|
||||
== Deprecated features
|
||||
|
||||
|
||||
@ -42,6 +42,8 @@ public class AdminRoles {
|
||||
public static final String VIEW_EVENTS = "view-events";
|
||||
public static final String VIEW_IDENTITY_PROVIDERS = "view-identity-providers";
|
||||
public static final String VIEW_AUTHORIZATION = "view-authorization";
|
||||
@Deprecated(since = "26.4", forRemoval = true)
|
||||
public static final String VIEW_SYSTEM = "view-system";
|
||||
|
||||
public static final String MANAGE_REALM = "manage-realm";
|
||||
public static final String MANAGE_USERS = "manage-users";
|
||||
|
||||
@ -291,7 +291,7 @@ public class AdminRoot {
|
||||
|
||||
Cors.builder().allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().add();
|
||||
|
||||
return new ServerInfoAdminResource(session);
|
||||
return new ServerInfoAdminResource(session, auth);
|
||||
}
|
||||
|
||||
private HttpRequest getHttpRequest() {
|
||||
|
||||
@ -35,7 +35,9 @@ import org.keycloak.crypto.ClientSignatureVerifierProvider;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.policy.PasswordPolicyProvider;
|
||||
import org.keycloak.policy.PasswordPolicyProviderFactory;
|
||||
@ -64,7 +66,10 @@ import org.keycloak.representations.info.ServerInfoRepresentation;
|
||||
import org.keycloak.representations.info.SpiInfoRepresentation;
|
||||
import org.keycloak.representations.info.SystemInfoRepresentation;
|
||||
import org.keycloak.representations.info.ThemeInfoRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||
import org.keycloak.services.resources.admin.AdminAuth;
|
||||
import org.keycloak.services.resources.admin.fgap.AdminPermissions;
|
||||
import org.keycloak.theme.Theme;
|
||||
|
||||
import jakarta.ws.rs.GET;
|
||||
@ -94,9 +99,11 @@ public class ServerInfoAdminResource {
|
||||
private static final Map<String, List<String>> ENUMS = createEnumsMap(EventType.class, OperationType.class, ResourceType.class);
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final AdminAuth auth;
|
||||
|
||||
public ServerInfoAdminResource(KeycloakSession session) {
|
||||
public ServerInfoAdminResource(KeycloakSession session, AdminAuth auth) {
|
||||
this.session = session;
|
||||
this.auth = auth;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,9 +118,14 @@ public class ServerInfoAdminResource {
|
||||
@Operation( summary = "Get themes, social providers, auth providers, and event listeners available on this server")
|
||||
public ServerInfoRepresentation getInfo() {
|
||||
ServerInfoRepresentation info = new ServerInfoRepresentation();
|
||||
info.setSystemInfo(SystemInfoRepresentation.create(session.getKeycloakSessionFactory().getServerStartupTimestamp(), Version.VERSION));
|
||||
info.setCpuInfo(CpuInfoRepresentation.create());
|
||||
info.setMemoryInfo(MemoryInfoRepresentation.create());
|
||||
RealmModel userRealm = session.getContext().getRealm();
|
||||
if (RealmManager.isAdministrationRealm(userRealm)
|
||||
|| AdminPermissions.evaluator(session, userRealm, auth).hasOneAdminRole(AdminRoles.VIEW_SYSTEM)) {
|
||||
// system information is only for admins in the administration realm or fallback view-system role
|
||||
info.setSystemInfo(SystemInfoRepresentation.create(session.getKeycloakSessionFactory().getServerStartupTimestamp(), Version.VERSION));
|
||||
info.setCpuInfo(CpuInfoRepresentation.create());
|
||||
info.setMemoryInfo(MemoryInfoRepresentation.create());
|
||||
}
|
||||
info.setProfileInfo(createProfileInfo());
|
||||
info.setFeatures(createFeatureRepresentations());
|
||||
|
||||
|
||||
@ -17,11 +17,16 @@
|
||||
|
||||
package org.keycloak.tests.admin;
|
||||
|
||||
import jakarta.ws.rs.ForbiddenException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jgroups.util.UUID;
|
||||
import org.junit.Assert;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.UserModel;
|
||||
@ -40,6 +45,7 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.info.ServerInfoRepresentation;
|
||||
import org.keycloak.services.resources.admin.AdminAuth.Resource;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
@ -52,6 +58,7 @@ import org.keycloak.testsuite.util.FederatedIdentityBuilder;
|
||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||
import org.keycloak.testframework.realm.RoleConfigBuilder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@ -547,6 +554,51 @@ public class PermissionsTest extends AbstractPermissionsTest {
|
||||
invoke(realm -> realm.localization().deleteRealmLocalizationTexts("en"), clients.get("REALM2"), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerInfo() throws Exception {
|
||||
// user in master with no permission => forbidden
|
||||
Assert.assertThrows(ForbiddenException.class, () -> clients.get("master-none").serverInfo().getInfo());
|
||||
// user in master with any permission can see the system info
|
||||
ServerInfoRepresentation serverInfo = clients.get("master-view-realm").serverInfo().getInfo();
|
||||
Assert.assertNotNull(serverInfo.getSystemInfo());
|
||||
Assert.assertNotNull(serverInfo.getCpuInfo());
|
||||
Assert.assertNotNull(serverInfo.getMemoryInfo());
|
||||
|
||||
// user in test realm with no permission => forbidden
|
||||
Assert.assertThrows(ForbiddenException.class, () -> clients.get("none").serverInfo().getInfo());
|
||||
// user in test realm with any permission cannot see the system info
|
||||
serverInfo = clients.get("view-realm").serverInfo().getInfo();
|
||||
Assert.assertNull(serverInfo.getSystemInfo());
|
||||
Assert.assertNull(serverInfo.getCpuInfo());
|
||||
Assert.assertNull(serverInfo.getMemoryInfo());
|
||||
serverInfo = clients.get("manage-users").serverInfo().getInfo();
|
||||
Assert.assertNull(serverInfo.getSystemInfo());
|
||||
Assert.assertNull(serverInfo.getCpuInfo());
|
||||
Assert.assertNull(serverInfo.getMemoryInfo());
|
||||
|
||||
// assign the view-system permission to a test realm user and check the fallback works
|
||||
ClientRepresentation realmMgtRep = adminClient.realm(REALM_NAME).clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0);
|
||||
ClientResource realmMgtRes = adminClient.realm(REALM_NAME).clients().get(realmMgtRep.getId());
|
||||
RoleRepresentation viewSystem = new RoleRepresentation();
|
||||
viewSystem.setName(AdminRoles.VIEW_SYSTEM);
|
||||
realmMgtRes.roles().create(viewSystem);
|
||||
viewSystem = realmMgtRes.roles().get(AdminRoles.VIEW_SYSTEM).toRepresentation();
|
||||
UserRepresentation userRep = adminClient.realm(REALM_NAME).users().search("view-realm", Boolean.TRUE).get(0);
|
||||
UserResource userRes = adminClient.realm(REALM_NAME).users().get(userRep.getId());
|
||||
userRes.roles().clientLevel(realmMgtRep.getId()).add(Collections.singletonList(viewSystem));
|
||||
try (Keycloak keycloak = adminClientFactory.create().realm(REALM_NAME)
|
||||
.username(userRep.getUsername()).password("password").clientId("test-client")
|
||||
.build()) {
|
||||
serverInfo = keycloak.serverInfo().getInfo();
|
||||
Assert.assertNotNull(serverInfo.getSystemInfo());
|
||||
Assert.assertNotNull(serverInfo.getCpuInfo());
|
||||
Assert.assertNotNull(serverInfo.getMemoryInfo());
|
||||
} finally {
|
||||
userRes.roles().clientLevel(realmMgtRep.getId()).remove(Collections.singletonList(viewSystem));
|
||||
realmMgtRes.roles().get(AdminRoles.VIEW_SYSTEM).remove();
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyAnyAdminRoleReqired(Invocation invocation) {
|
||||
invoke(invocation, clients.get("view-realm"), true);
|
||||
invoke(invocation, clients.get("manage-realm"), true);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user