Move ServiceAccountClientTest.java to the new testsuite

Part of: #34494

Signed-off-by: Simon Vacek <simonvacky@email.cz>
This commit is contained in:
Simon Vacek 2025-03-12 11:32:25 +01:00 committed by Stian Thorgersen
parent e8d3d142df
commit e1fdd1dab6
6 changed files with 108 additions and 82 deletions

View File

@ -70,8 +70,8 @@ public class ClientConfigBuilder {
}
public ClientConfigBuilder serviceAccount() {
rep.setServiceAccountsEnabled(true);
public ClientConfigBuilder serviceAccountsEnabled(boolean enabled) {
rep.setServiceAccountsEnabled(enabled);
return this;
}
@ -85,6 +85,11 @@ public class ClientConfigBuilder {
return this;
}
public ClientConfigBuilder authenticatorType(String authenticatorType) {
rep.setClientAuthenticatorType(authenticatorType);
return this;
}
public ClientConfigBuilder attribute(String key, String value) {
if (rep.getAttributes() == null) {
rep.setAttributes(new HashMap<>());

View File

@ -8,7 +8,7 @@ public class DefaultOAuthClientConfiguration implements ClientConfig {
@Override
public ClientConfigBuilder configure(ClientConfigBuilder client) {
return client.clientId("test-app")
.serviceAccount()
.serviceAccountsEnabled(true)
.directAccessGrants()
.secret("test-secret");
}

View File

@ -23,7 +23,7 @@ public class AuthzClientConfig implements ClientConfig {
@Override
public ClientConfigBuilder configure(ClientConfigBuilder client) {
return client.serviceAccount()
return client.serviceAccountsEnabled(true)
.authorizationServices();
}

View File

@ -32,11 +32,8 @@ import javax.xml.parsers.ParserConfigurationException;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.Response;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
@ -45,11 +42,9 @@ import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.saml.SamlConfigAttributes;
import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.protocol.saml.installation.SamlSPDescriptorClientInstallation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.testframework.annotations.InjectAdminClient;
import org.keycloak.testframework.annotations.InjectAdminEvents;
import org.keycloak.testframework.annotations.InjectClient;
import org.keycloak.testframework.annotations.InjectKeycloakUrls;
@ -61,8 +56,6 @@ import org.keycloak.testframework.realm.ClientConfig;
import org.keycloak.testframework.realm.ClientConfigBuilder;
import org.keycloak.testframework.realm.ManagedClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.realm.RealmConfig;
import org.keycloak.testframework.realm.RealmConfigBuilder;
import org.keycloak.testframework.server.KeycloakServerConfig;
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
import org.keycloak.testframework.server.KeycloakUrls;
@ -449,7 +442,7 @@ public class InstallationTest {
.bearerOnly(false)
.publicClient(false)
.authorizationServices()
.serviceAccount();
.serviceAccountsEnabled(true);
}
}

View File

@ -15,94 +15,131 @@
* the License.
*/
package org.keycloak.testsuite.admin.client;
package org.keycloak.tests.admin.client;
import java.util.stream.Collectors;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testframework.annotations.InjectClient;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.realm.ClientConfig;
import org.keycloak.testframework.realm.ClientConfigBuilder;
import org.keycloak.testframework.realm.ManagedClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.tests.utils.admin.ApiUtil;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
import java.util.stream.Collectors;
/**
*
* @author rmartinc
*/
public class ServiceAccountClientTest extends AbstractClientTest {
@KeycloakIntegrationTest
public class ServiceAccountClientTest {
@InjectRealm
ManagedRealm managedRealm;
@InjectClient(config = ServiceAccountClientConfig.class)
ManagedClient managedClient;
@InjectOAuthClient
OAuthClient oAuthClient;
private static final String clientId = "service-account-client";
@Test
public void testServiceAccountEnableDisable() throws Exception {
ClientScopeRepresentation serviceAccountScope = ApiUtil.findClientScopeByName(
testRealmResource(), ServiceAccountConstants.SERVICE_ACCOUNT_SCOPE).toRepresentation();
public void testServiceAccountEnableDisable() {
ClientScopeResource serviceAccountScopeRsc = ApiUtil.findClientScopeByName(
managedRealm.admin(), ServiceAccountConstants.SERVICE_ACCOUNT_SCOPE);
Assertions.assertNotNull(serviceAccountScopeRsc);
ClientScopeRepresentation serviceAccountScope = serviceAccountScopeRsc.toRepresentation();
// Create a client with service account enabled
ClientRepresentation clientRep = new ClientRepresentation();
clientRep.setClientId("service-account-client");
clientRep.setProtocol("openid-connect");
clientRep.setSecret("password");
clientRep.setServiceAccountsEnabled(Boolean.TRUE);
clientRep.setClientAuthenticatorType("client-secret");
clientRep.setPublicClient(Boolean.FALSE);
String clientUuid = createClient(clientRep);
ClientResource client = testRealmResource().clients().get(clientUuid);
getCleanup().addClientUuid(clientUuid);
MatcherAssert.assertThat(client.getDefaultClientScopes().stream().map(ClientScopeRepresentation::getName).collect(Collectors.toList()),
Matchers.hasItem("service_account"));
MatcherAssert.assertThat(
managedClient.admin().getDefaultClientScopes().stream()
.map(ClientScopeRepresentation::getName)
.collect(Collectors.toList()),
Matchers.hasItem("service_account")
);
// perform a login and check the claims are there
oauth.client("service-account-client", "password");
AccessTokenResponse response = oauth.doClientCredentialsGrantAccessTokenRequest();
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals("service-account-client", accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ID));
Assert.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_HOST));
Assert.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ADDRESS));
oAuthClient.client(clientId, "password");
AccessTokenResponse response = oAuthClient.doClientCredentialsGrantAccessTokenRequest();
AccessToken accessToken = oAuthClient.verifyToken(response.getAccessToken());
Assertions.assertEquals(clientId, accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ID));
Assertions.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_HOST));
Assertions.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ADDRESS));
// update the client to remove service account
clientRep.setServiceAccountsEnabled(Boolean.FALSE);
client.update(clientRep);
MatcherAssert.assertThat(client.getDefaultClientScopes().stream().map(ClientScopeRepresentation::getName).collect(Collectors.toList()),
Matchers.not(Matchers.hasItem(ServiceAccountConstants.SERVICE_ACCOUNT_SCOPE)));
response = oauth.doClientCredentialsGrantAccessTokenRequest();
Assert.assertEquals("unauthorized_client", response.getError());
managedClient.updateWithCleanup(c -> c.serviceAccountsEnabled(false));
MatcherAssert.assertThat(
managedClient.admin().getDefaultClientScopes().stream()
.map(ClientScopeRepresentation::getName)
.collect(Collectors.toList()),
Matchers.not(Matchers.hasItem(ServiceAccountConstants.SERVICE_ACCOUNT_SCOPE))
);
response = oAuthClient.doClientCredentialsGrantAccessTokenRequest();
Assertions.assertEquals("unauthorized_client", response.getError());
// re-enable sevice accounts
clientRep.setServiceAccountsEnabled(Boolean.TRUE);
client.update(clientRep);
MatcherAssert.assertThat(client.getDefaultClientScopes().stream().map(ClientScopeRepresentation::getName).collect(Collectors.toList()),
Matchers.hasItem(ServiceAccountConstants.SERVICE_ACCOUNT_SCOPE));
response = oauth.doClientCredentialsGrantAccessTokenRequest();
accessToken = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals("service-account-client", accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ID));
Assert.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_HOST));
Assert.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ADDRESS));
managedClient.updateWithCleanup(c -> c.serviceAccountsEnabled(true));
MatcherAssert.assertThat(
managedClient.admin().getDefaultClientScopes().stream()
.map(ClientScopeRepresentation::getName)
.collect(Collectors.toList()),
Matchers.hasItem(ServiceAccountConstants.SERVICE_ACCOUNT_SCOPE)
);
response = oAuthClient.doClientCredentialsGrantAccessTokenRequest();
accessToken = oAuthClient.verifyToken(response.getAccessToken());
Assertions.assertEquals("service-account-client", accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ID));
Assertions.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_HOST));
Assertions.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ADDRESS));
// assign the scope as optional
client.removeDefaultClientScope(serviceAccountScope.getId());
client.addOptionalClientScope(serviceAccountScope.getId());
managedClient.admin().removeDefaultClientScope(serviceAccountScope.getId());
managedClient.admin().addOptionalClientScope(serviceAccountScope.getId());
// re-enable service accounts, should assign the scope again as default
clientRep.setServiceAccountsEnabled(Boolean.TRUE);
client.update(clientRep);
MatcherAssert.assertThat(client.getDefaultClientScopes().stream().map(ClientScopeRepresentation::getName).collect(Collectors.toList()),
Matchers.hasItem(ServiceAccountConstants.SERVICE_ACCOUNT_SCOPE));
response = oauth.doClientCredentialsGrantAccessTokenRequest();
accessToken = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals("service-account-client", accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ID));
Assert.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_HOST));
Assert.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ADDRESS));
managedClient.updateWithCleanup(c -> c.serviceAccountsEnabled(true));
MatcherAssert.assertThat(
managedClient.admin().getDefaultClientScopes().stream()
.map(ClientScopeRepresentation::getName)
.collect(Collectors.toList()),
Matchers.hasItem(ServiceAccountConstants.SERVICE_ACCOUNT_SCOPE)
);
response = oAuthClient.doClientCredentialsGrantAccessTokenRequest();
accessToken = oAuthClient.verifyToken(response.getAccessToken());
Assertions.assertEquals("service-account-client", accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ID));
Assertions.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_HOST));
Assertions.assertNotNull(accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ADDRESS));
// remove the service account and client credentials should fail
UserRepresentation serviceAccountUser = client.getServiceAccountUser();
testRealmResource().users().delete(serviceAccountUser.getId());
response = oauth.doClientCredentialsGrantAccessTokenRequest();
Assert.assertEquals("invalid_request", response.getError());
UserRepresentation serviceAccountUser = managedClient.admin().getServiceAccountUser();
managedRealm.admin().users().delete(serviceAccountUser.getId()).close();
response = oAuthClient.doClientCredentialsGrantAccessTokenRequest();
Assertions.assertEquals("invalid_request", response.getError());
}
private static class ServiceAccountClientConfig implements ClientConfig {
@Override
public ClientConfigBuilder configure(ClientConfigBuilder client) {
return client
.clientId(clientId)
.protocol("openid-connect")
.secret("password")
.serviceAccountsEnabled(true)
.authenticatorType("client-secret")
.publicClient(false);
}
}
}

View File

@ -67,15 +67,6 @@ public class ApiUtil {
return null;
}
public static ClientResource findClientResourceByClientId(RealmResource realm, String clientId) {
for (ClientRepresentation c : realm.clients().findAll()) {
if (c.getClientId().equals(clientId)) {
return realm.clients().get(c.getId());
}
}
return null;
}
public static ClientResource findClientResourceByName(RealmResource realm, String name) {
for (ClientRepresentation c : realm.clients().findAll()) {
if (name.equals(c.getName())) {