Add scope parameter to admin-client TokenManager.

Closes #10759
This commit is contained in:
Teubner, Malte 2022-03-31 12:05:32 +02:00 committed by Pedro Igor
parent 1b36251a23
commit b5f70d8a32
2 changed files with 85 additions and 31 deletions

View file

@ -56,14 +56,21 @@ public class AdminClientUtil {
public static final int NUMBER_OF_CONNECTIONS = 10;
public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String authServerContextRoot) throws Exception {
return createAdminClient(ignoreUnknownProperties, authServerContextRoot, MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID, null);
return createAdminClient(ignoreUnknownProperties, authServerContextRoot, MASTER, ADMIN, ADMIN,
Constants.ADMIN_CLI_CLIENT_ID, null, null);
}
public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String realmName, String username, String password, String clientId, String clientSecret) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
return createAdminClient(ignoreUnknownProperties, getAuthServerContextRoot(), realmName, username, password, clientId, clientSecret);
public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String realmName, String username,
String password, String clientId, String clientSecret) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
return createAdminClient(ignoreUnknownProperties, getAuthServerContextRoot(), realmName, username, password,
clientId, clientSecret, null);
}
public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String authServerContextRoot, String realmName, String username, String password, String clientId, String clientSecret) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String authServerContextRoot, String realmName,
String username, String password, String clientId, String clientSecret, String scope)
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
ResteasyClient resteasyClient = createResteasyClient(ignoreUnknownProperties, null);
return KeycloakBuilder.builder()
@ -73,10 +80,13 @@ public class AdminClientUtil {
.password(password)
.clientId(clientId)
.clientSecret(clientSecret)
.resteasyClient(resteasyClient).build();
.resteasyClient(resteasyClient)
.scope(scope).build();
}
public static Keycloak createAdminClientWithClientCredentials(String realmName, String clientId, String clientSecret) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
public static Keycloak createAdminClientWithClientCredentials(String realmName, String clientId, String clientSecret, String scope)
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
boolean ignoreUnknownProperties = false;
ResteasyClient resteasyClient = createResteasyClient(ignoreUnknownProperties, null);
@ -86,7 +96,8 @@ public class AdminClientUtil {
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(clientId)
.clientSecret(clientSecret)
.resteasyClient(resteasyClient).build();
.resteasyClient(resteasyClient)
.scope(scope).build();
}
public static Keycloak createAdminClient() throws Exception {

View file

@ -19,25 +19,28 @@
package org.keycloak.testsuite.admin;
import java.util.List;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.core.Response;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.Constants;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.util.AdminClientUtil;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.ClientScopeBuilder;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder;
@ -48,9 +51,15 @@ import org.keycloak.testsuite.util.UserBuilder;
*/
public class AdminClientTest extends AbstractKeycloakTest {
private static String realmName;
private static String userId;
private static String userName;
private static String clientUUID;
private static String clientId;
private static String clientSecret;
@Rule
public AssertEvents events = new AssertEvents(this);
@ -65,17 +74,19 @@ public class AdminClientTest extends AbstractKeycloakTest {
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
RealmBuilder realm = RealmBuilder.create().name("test")
realmName = "test";
RealmBuilder realm = RealmBuilder.create().name(realmName)
.privateKey("MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=")
.publicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB")
.testEventListener();
clientUUID = KeycloakModelUtils.generateId();
clientId = "service-account-cl";
clientSecret = "secret1";
ClientRepresentation enabledAppWithSkipRefreshToken = ClientBuilder.create()
.id(KeycloakModelUtils.generateId())
.clientId("service-account-cl")
.secret("secret1")
.id(clientUUID)
.clientId(clientId)
.secret(clientSecret)
.serviceAccountsEnabled(true)
.build();
realm.client(enabledAppWithSkipRefreshToken);
@ -101,54 +112,54 @@ public class AdminClientTest extends AbstractKeycloakTest {
@Test
public void clientCredentialsAuthSuccess() throws Exception {
try (Keycloak adminClient = AdminClientUtil.createAdminClientWithClientCredentials("test", "service-account-cl", "secret1")) {
try (Keycloak adminClient = AdminClientUtil.createAdminClientWithClientCredentials(realmName, clientId, clientSecret, null)) {
// Check possible to load the realm
RealmRepresentation realm = adminClient.realm("test").toRepresentation();
Assert.assertEquals("test", realm.getRealm());
RealmRepresentation realm = adminClient.realm(realmName).toRepresentation();
Assert.assertEquals(realmName, realm.getRealm());
setTimeOffset(1000);
// Check still possible to load the realm after original token expired (admin client should automatically re-authenticate)
realm = adminClient.realm("test").toRepresentation();
Assert.assertEquals("test", realm.getRealm());
realm = adminClient.realm(realmName).toRepresentation();
Assert.assertEquals(realmName, realm.getRealm());
}
}
@Test
public void clientCredentialsClientDisabled() throws Exception {
try (Keycloak adminClient = AdminClientUtil.createAdminClientWithClientCredentials("test", "service-account-cl", "secret1")) {
try (Keycloak adminClient = AdminClientUtil.createAdminClientWithClientCredentials(realmName, clientId, clientSecret, null)) {
// Check possible to load the realm
RealmRepresentation realm = adminClient.realm("test").toRepresentation();
Assert.assertEquals("test", realm.getRealm());
RealmRepresentation realm = adminClient.realm(realmName).toRepresentation();
Assert.assertEquals(realmName, realm.getRealm());
// Disable client and check it should not be possible to load the realms anymore
setClientEnabled("service-account-cl", false);
setClientEnabled(clientId, false);
// Check not possible to invoke anymore
try {
realm = adminClient.realm("test").toRepresentation();
realm = adminClient.realm(realmName).toRepresentation();
Assert.fail("Not expected to successfully get realm");
} catch (NotAuthorizedException nae) {
// Expected
}
} finally {
setClientEnabled("service-account-cl", true);
setClientEnabled(clientId, true);
}
}
@Test
public void adminAuthClientDisabled() throws Exception {
try (Keycloak adminClient = AdminClientUtil.createAdminClient(false, "test", "test-user@localhost", "password", Constants.ADMIN_CLI_CLIENT_ID, null)) {
try (Keycloak adminClient = AdminClientUtil.createAdminClient(false, realmName, "test-user@localhost", "password", Constants.ADMIN_CLI_CLIENT_ID, null)) {
// Check possible to load the realm
RealmRepresentation realm = adminClient.realm("test").toRepresentation();
Assert.assertEquals("test", realm.getRealm());
RealmRepresentation realm = adminClient.realm(realmName).toRepresentation();
Assert.assertEquals(realmName, realm.getRealm());
// Disable client and check it should not be possible to load the realms anymore
setClientEnabled(Constants.ADMIN_CLI_CLIENT_ID, false);
// Check not possible to invoke anymore
try {
realm = adminClient.realm("test").toRepresentation();
realm = adminClient.realm(realmName).toRepresentation();
Assert.fail("Not expected to successfully get realm");
} catch (NotAuthorizedException nae) {
// Expected
@ -158,10 +169,42 @@ public class AdminClientTest extends AbstractKeycloakTest {
}
}
@Test
public void scopedClientCredentialsAuthSuccess() throws Exception {
final RealmResource testRealm = adminClient.realm(realmName);
// we need to create custom scope after import, otherwise the default scopes are missing.
final String scopeName = "myScope";
final String scopeId = KeycloakModelUtils.generateId();
createScope(testRealm, scopeName, scopeId);
testRealm.clients().get(clientUUID).addOptionalClientScope(scopeId);
// with scope
try (Keycloak adminClient = AdminClientUtil.createAdminClientWithClientCredentials(realmName,
clientId, clientSecret, scopeName)) {
final AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
Assert.assertTrue(accessToken.getScope().contains(scopeName));
}
// without scope
try (Keycloak adminClient = AdminClientUtil.createAdminClientWithClientCredentials(realmName,
clientId, clientSecret, null)) {
final AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
Assert.assertFalse(accessToken.getScope().contains(scopeName));
}
}
private void setClientEnabled(String clientId, boolean enabled) {
ClientResource client = ApiUtil.findClientByClientId(adminClient.realms().realm("test"), clientId);
ClientResource client = ApiUtil.findClientByClientId(adminClient.realms().realm(realmName), clientId);
ClientRepresentation clientRep = client.toRepresentation();
clientRep.setEnabled(enabled);
client.update(clientRep);
}
private void createScope(RealmResource testRealm, String scopeName, String scopeId) {
final ClientScopeRepresentation testScope =
ClientScopeBuilder.create().name(scopeName).protocol("openid-connect").build();
testScope.setId(scopeId);
final Response scope = testRealm.clientScopes().create(testScope);
Assert.assertEquals(201, scope.getStatus());
}
}