8000 Add scope parameter to admin-client TokenManager. by mteubner · Pull Request #10984 · 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
8000
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ public class Config {
private String clientId;
private String clientSecret;
private String grantType;
private String scope;

public Config(String serverUrl, String realm, String username, String password, String clientId, String clientSecret) {
this(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD);
this(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, null);
}

public Config(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, String grantType) {
public Config(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, String grantType, String scope) {
this.serverUrl = serverUrl;
this.realm = realm;
this.username = username;
Expand All @@ -46,6 +47,7 @@ public Config(String serverUrl, String realm, String username, String password,
this.clientSecret = clientSecret;
this.grantType = grantType;
checkGrantType(grantType);
this.scope = scope;
}

public String getServerUrl() {
Expand Down Expand Up @@ -100,6 +102,14 @@ public boolean isPublicClient() {
return clientSecret == null;
}

public String getScope() {
return scope;
}

public void setScope(String scope) {
this.scope = scope;
}

public String getGrantType() {
return grantType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ public static ResteasyClientProvider getClientProvider() {
private final Client client;
private boolean closed = false;

Keycloak(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, String grantType, Client resteasyClient, String authtoken) {
config = new Config(serverUrl, realm, username, password, clientId, clientSecret, grantType);
Keycloak(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, String grantType, Client resteasyClient, String authtoken, String scope) {
config = new Config(serverUrl, realm, username, password, clientId, clientSecret, grantType, scope);
client = resteasyClient != null ? resteasyClient : newRestEasyClient(null, null, false);
authToken = authtoken;
tokenManager = authtoken == null ? new TokenManager(config, client) : null;
Expand All @@ -101,9 +101,13 @@ private static Client newRestEasyClient(Object customJacksonProvider, SSLContext
private BearerAuthFilter newAuthFilter() {
return authToken != null ? new BearerAuthFilter(authToken) : new BearerAuthFilter(tokenManager);
}

public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext, Object customJacksonProvider, boolean disableTrustManager, String authToken, String scope) {
return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, newRestEasyClient(customJacksonProvider, sslContext, disableTrustManager), authToken, scope);
}

public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext, Object customJacksonProvider, boolean disableTrustManager, String authToken) {
return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, newRestEasyClient(customJacksonProvider, sslContext, disableTrustManager), authToken);
return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, newRestEasyClient(customJacksonProvider, sslContext, disableTrustManager), authToken, null);
}

public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public class KeycloakBuilder {
private String grantType;
private Client resteasyClient;
private String authorization;
private String scope;

public KeycloakBuilder serverUrl(String serverUrl) {
this.serverUrl = serverUrl;
Expand Down Expand Up @@ -94,6 +95,11 @@ public KeycloakBuilder clientId(String clientId) {
return this;
}

public KeycloakBuilder scope(String scope) {
this.scope = scope;
return this;
}

public KeycloakBuilder clientSecret(String clientSecret) {
this.clientSecret = clientSecret;
return this;
Expand Down Expand Up @@ -143,7 +149,7 @@ public Keycloak build() {
throw new IllegalStateException("clientId required");
}

return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, grantType, resteasyClient, authorization);
return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, grantType, resteasyClient, authorization, scope);
}

private KeycloakBuilder() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
import static org.keycloak.OAuth2Constants.GRANT_TYPE;
import static org.keycloak.OAuth2Constants.PASSWORD;
import static org.keycloak.OAuth2Constants.REFRESH_TOKEN;
import static org.keycloak.OAuth2Constants.SCOPE;
import static org.keycloak.OAuth2Constants.USERNAME;

/**
* @author rodrigo.sasaki@icarros.com.br
Expand Down Expand Up @@ -79,8 +81,12 @@ public synchronized AccessTokenResponse getAccessToken() {
public AccessTokenResponse grantToken() {
Form form = new Form().param(GRANT_TYPE, accessTokenGrantType);
if (PASSWORD.equals(accessTokenGrantType)) {
form.param("username", config.getUsername())
.param("password", config.getPassword());
form.param(USERNAME, config.getUsername())
.param(PASSWORD, config.getPassword());
}

if (config.getScope() != null) {
form.param(SCOPE, config.getScope());
}

if (config.isPublicClient()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 i 8BD0 gnoreUnknownProperties, 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()
Expand All @@ -73,10 +80,13 @@ public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String
.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);

Expand All @@ -86,7 +96,8 @@ public static Keycloak createAdminClientWithClientCredentials(String realmName,
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(clientId)
.clientSecret(clientSecret)
.resteasyClient(resteasyClient).build();
.resteasyClient(resteasyClient)
.scope(scope).build();
}

public static Keycloak createAdminClient() throws Exception {
Expand Down
3B52
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -48,9 +51,15 @@
*/
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);

Expand All @@ -65,17 +74,19 @@ public void beforeAbstractKeycloakTest() throws Exception {

@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);
Expand All @@ -101,54 +112,54 @@ public void addTestRealms(List<RealmRepresentation> testRealms) {

@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
Expand All @@ -158,10 +169,42 @@ public void adminAuthClientDisabled() throws Exception {
}
}

@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());
}
}
0