Allow admin console whoami endpoint to applications that have a special attribute

Closes #29640

Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
rmartinc 2024-05-17 13:31:48 +02:00 committed by Hynek Mlnařík
parent cb3f248d73
commit 3304540855
4 changed files with 39 additions and 4 deletions

View file

@ -22,7 +22,9 @@
"publicClient": true,
"frontchannelLogout": false,
"protocol": "openid-connect",
"attributes": {},
"attributes": {
"security.admin.console": "true"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
@ -44,4 +46,4 @@
"configure": true,
"manage": true
}
}
}

View file

@ -171,4 +171,7 @@ public final class Constants {
// Sent to clients when authentication session expired, but user is already logged-in in current browser
public static final String AUTHENTICATION_EXPIRED_MESSAGE = "authentication_expired";
// attribute name used in apps to mark that it is an admin console and its azp is allowed
public static final String SECURITY_ADMIN_CONSOLE_ATTR = "security.admin.console";
}

View file

@ -216,8 +216,16 @@ public class AdminConsole {
throw new NotAuthorizedException("Bearer");
}
if (!Constants.ADMIN_CONSOLE_CLIENT_ID.equals(authResult.getToken().getIssuedFor())) {
throw new ForbiddenException("Token not valid for admin console");
final String issuedFor = authResult.getToken().getIssuedFor();
if (!Constants.ADMIN_CONSOLE_CLIENT_ID.equals(issuedFor)) {
if (issuedFor == null) {
throw new ForbiddenException("No azp claim in the token");
}
// check the attribute to see if the app is defined as an admin console
ClientModel client = session.clients().getClientByClientId(realm, issuedFor);
if (client == null || !Boolean.parseBoolean(client.getAttribute(Constants.SECURITY_ADMIN_CONSOLE_ATTR))) {
throw new ForbiddenException("Token issued for an application that is not the admin console: " + issuedFor);
}
}
UserModel user= authResult.getUser();

View file

@ -25,6 +25,7 @@ import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.broker.util.SimpleHttpDefault;
import org.keycloak.testsuite.console.page.AdminConsole;
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
import org.keycloak.testsuite.util.AdminClientUtil;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder;
@ -289,4 +290,25 @@ public class AdminConsoleWhoAmILocaleTest extends AbstractKeycloakTest {
}
}
}
@Test
public void testLocaleRealmTokenForOtherClientButAllowed() throws Exception {
try (ClientAttributeUpdater updater = ClientAttributeUpdater.forClient(adminClient, REALM_I18N_ON, Constants.ADMIN_CLI_CLIENT_ID)
.setAttribute(Constants.SECURITY_ADMIN_CONSOLE_ATTR, Boolean.TRUE.toString())
.update();
Keycloak adminCliClient = AdminClientUtil.createAdminClient(true, REALM_I18N_ON,
USER_WITH_LOCALE, PASSWORD, Constants.ADMIN_CLI_CLIENT_ID, null)) {
AccessTokenResponse accessToken = adminCliClient.tokenManager().getAccessToken();
Assert.assertNotNull(accessToken);
String token = accessToken.getToken();
JsonNode whoAmI = SimpleHttpDefault
.doGet(whoAmiUrl(REALM_I18N_ON), client)
.header("Accept", "application/json")
.auth(token)
.asJson();
Assert.assertEquals(REALM_I18N_ON, whoAmI.get("realm").asText());
Assert.assertEquals(USER_LOCALE, whoAmI.get("locale").asText());
checkRealmAccess(REALM_I18N_ON, whoAmI);
}
}
}