mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Move AdminClientTest to the new testsuite (#44705)
* Moving files to the new test suite Signed-off-by: Simon Vacek <simonvacky@email.cz> * Move AdminClientTest to the new testsuite Part of: #35040 Signed-off-by: Simon Vacek <simonvacky@email.cz> Co-authored: Lukas Hanusovsky <lhanusov@redhat.com> * Refactoring of ManagedCertificates * Fix compatiblity issue with ManagedCertificates dependency Signed-off-by: stianst <stianst@gmail.com> * Fixing trustStrategy for SSLContext truststore. Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com> * Fix FIPS Signed-off-by: stianst <stianst@gmail.com> --------- Signed-off-by: Simon Vacek <simonvacky@email.cz> Signed-off-by: stianst <stianst@gmail.com> Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com> Co-authored-by: Simon Vacek <simonvacky@email.cz> Co-authored-by: stianst <stianst@gmail.com>
This commit is contained in:
parent
ca617d9711
commit
92849ef5d3
@ -54,7 +54,7 @@ public class ClusteredKeycloakServer implements KeycloakServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(KeycloakServerConfigBuilder configBuilder) {
|
||||
public void start(KeycloakServerConfigBuilder configBuilder, boolean tlsEnabled) {
|
||||
int numServers = containers.length;
|
||||
CountdownLatchLoggingConsumer clusterLatch = new CountdownLatchLoggingConsumer(numServers, String.format(CLUSTER_VIEW_REGEX, numServers));
|
||||
String[] imagePeServer = null;
|
||||
@ -155,11 +155,6 @@ public class ClusteredKeycloakServer implements KeycloakServer {
|
||||
return getManagementBaseUrl(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTlsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getBasePort(int index) {
|
||||
return containers[index].getMappedPort(REQUEST_PORT);
|
||||
}
|
||||
|
||||
@ -14,11 +14,11 @@ public class AdminClientFactorySupplier implements Supplier<AdminClientFactory,
|
||||
@Override
|
||||
public AdminClientFactory getValue(InstanceContext<AdminClientFactory, InjectAdminClientFactory> instanceContext) {
|
||||
KeycloakServer server = instanceContext.getDependency(KeycloakServer.class);
|
||||
ManagedCertificates managedCert = instanceContext.getDependency(ManagedCertificates.class);
|
||||
|
||||
if (!server.isTlsEnabled()) {
|
||||
if (!managedCert.isTlsEnabled()) {
|
||||
return new AdminClientFactory(server.getBaseUrl());
|
||||
} else {
|
||||
ManagedCertificates managedCert = instanceContext.getDependency(ManagedCertificates.class);
|
||||
SSLContext sslContext = managedCert.getClientSSLContext();
|
||||
return new AdminClientFactory(server.getBaseUrl(), sslContext);
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ import org.keycloak.testframework.injection.InstanceContext;
|
||||
import org.keycloak.testframework.injection.LifeCycle;
|
||||
import org.keycloak.testframework.injection.RequestedInstance;
|
||||
import org.keycloak.testframework.injection.Supplier;
|
||||
import org.keycloak.testframework.server.KeycloakServer;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
@ -22,10 +21,9 @@ public class HttpClientSupplier implements Supplier<HttpClient, InjectHttpClient
|
||||
public HttpClient getValue(InstanceContext<HttpClient, InjectHttpClient> instanceContext) {
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
|
||||
KeycloakServer server = instanceContext.getDependency(KeycloakServer.class);
|
||||
if (server.isTlsEnabled()) {
|
||||
ManagedCertificates managedCerts = instanceContext.getDependency(ManagedCertificates.class);
|
||||
ManagedCertificates managedCerts = instanceContext.getDependency(ManagedCertificates.class);
|
||||
|
||||
if (managedCerts.isTlsEnabled()) {
|
||||
SSLContext sslContext = managedCerts.getClientSSLContext();
|
||||
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
|
||||
sslContext,
|
||||
|
||||
@ -5,6 +5,8 @@ import org.keycloak.common.util.KeystoreUtil;
|
||||
public class CertificatesConfigBuilder {
|
||||
|
||||
private KeystoreUtil.KeystoreFormat keystoreFormat = KeystoreUtil.KeystoreFormat.JKS;
|
||||
private boolean tlsEnabled = false;
|
||||
private boolean mTlsEnabled = false;
|
||||
|
||||
public CertificatesConfigBuilder() {
|
||||
}
|
||||
@ -17,4 +19,22 @@ public class CertificatesConfigBuilder {
|
||||
public KeystoreUtil.KeystoreFormat getKeystoreFormat() {
|
||||
return this.keystoreFormat;
|
||||
}
|
||||
|
||||
public CertificatesConfigBuilder tlsEnabled(boolean tlsEnabled) {
|
||||
this.tlsEnabled = tlsEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isTlsEnabled() {
|
||||
return tlsEnabled || mTlsEnabled;
|
||||
}
|
||||
|
||||
public CertificatesConfigBuilder mTlsEnabled(boolean mTlsEnabled) {
|
||||
this.mTlsEnabled = mTlsEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isMTlsEnabled() {
|
||||
return mTlsEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ public class CertificatesSupplier implements Supplier<ManagedCertificates, Injec
|
||||
|
||||
@Override
|
||||
public boolean compatible(InstanceContext<ManagedCertificates, InjectCertificates> a, RequestedInstance<ManagedCertificates, InjectCertificates> b) {
|
||||
return true;
|
||||
return a.getAnnotation().config().equals(b.getAnnotation().config());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -5,7 +5,6 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
@ -20,25 +19,32 @@ import org.keycloak.common.crypto.CryptoProvider;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.crypto.def.DefaultCryptoProvider;
|
||||
|
||||
import org.apache.http.conn.ssl.TrustAllStrategy;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
public class ManagedCertificates {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ManagedCertificates.class);
|
||||
private final static Path KEYSTORES_DIR = Path.of(System.getProperty("java.io.tmpdir"));
|
||||
|
||||
private static final String STORE_PASSWORD = "mysuperstrongstorepassword";
|
||||
private static final char[] STORE_PASSWORD_CHARS = STORE_PASSWORD.toCharArray();
|
||||
|
||||
private final boolean tlsEnabled;
|
||||
private final boolean mTlsEnabled;
|
||||
private final KeystoreUtil.KeystoreFormat keystoreFormat;
|
||||
|
||||
private final CryptoProvider cryptoProvider;
|
||||
|
||||
private KeyStore serverKeyStore;
|
||||
private KeyStore clientsTrustStore;
|
||||
private final Path serverKeystorePath;
|
||||
private final Path clientsTruststorePath;
|
||||
private final char[] password;
|
||||
private final Path serverTruststorePath;
|
||||
|
||||
private final static Path KEYSTORES_DIR = Path.of(System.getProperty("java.io.tmpdir"));
|
||||
private final static String PRV_KEY_ENTRY = "prvKey";
|
||||
public final static String CERT_ENTRY = "cert";
|
||||
private final Path clientKeystorePath;
|
||||
private KeyStore clientKeyStore;
|
||||
|
||||
private final Path clientTruststorePath;
|
||||
private KeyStore clientTrustStore;
|
||||
|
||||
private SSLContext clientSslContext;
|
||||
|
||||
public ManagedCertificates(CertificatesConfigBuilder configBuilder) throws ManagedCertificatesException {
|
||||
if (!CryptoIntegration.isInitialised()) {
|
||||
@ -46,87 +52,120 @@ public class ManagedCertificates {
|
||||
}
|
||||
cryptoProvider = CryptoIntegration.getProvider();
|
||||
|
||||
serverKeystorePath = KEYSTORES_DIR.resolve("kc-testing-server-keystore" + "." + configBuilder.getKeystoreFormat().getPrimaryExtension());
|
||||
clientsTruststorePath = KEYSTORES_DIR.resolve("kc-testing-clients-truststore" + "." + configBuilder.getKeystoreFormat().getPrimaryExtension());
|
||||
keystoreFormat = configBuilder.getKeystoreFormat();
|
||||
tlsEnabled = configBuilder.isTlsEnabled();
|
||||
mTlsEnabled = configBuilder.isMTlsEnabled();
|
||||
|
||||
password = configBuilder.getKeystoreFormat() == KeystoreUtil.KeystoreFormat.JKS ? "password".toCharArray() : "passwordpassword".toCharArray();
|
||||
serverKeystorePath = resolvePath("kc-testing-server-keystore");
|
||||
serverTruststorePath = resolvePath("kc-testing-server-truststore");
|
||||
|
||||
initServerCerts(configBuilder.getKeystoreFormat());
|
||||
}
|
||||
clientKeystorePath = resolvePath("kc-testing-client-keystore");
|
||||
clientTruststorePath = resolvePath("kc-testing-client-truststore");
|
||||
|
||||
public String getKeycloakServerKeyStorePath() {
|
||||
return serverKeystorePath.toString();
|
||||
}
|
||||
|
||||
public String getKeycloakServerKeyStorePassword() {
|
||||
return String.valueOf(password);
|
||||
}
|
||||
|
||||
public KeyStore getClientTrustStore() {
|
||||
return clientsTrustStore;
|
||||
}
|
||||
|
||||
public X509Certificate getKeycloakServerCertificate() {
|
||||
try {
|
||||
return (X509Certificate) serverKeyStore.getCertificate(CERT_ENTRY);
|
||||
} catch (KeyStoreException e) {
|
||||
throw new ManagedCertificatesException(e);
|
||||
if (!Files.exists(serverKeystorePath) || !Files.exists(serverTruststorePath) || !Files.exists(clientKeystorePath) || !Files.exists(clientTruststorePath)) {
|
||||
createStores();
|
||||
} else {
|
||||
clientKeyStore = load(clientKeystorePath);
|
||||
clientTrustStore = load(clientTruststorePath);
|
||||
}
|
||||
|
||||
clientSslContext = tlsEnabled ? createClientSSLContext() : null;
|
||||
}
|
||||
|
||||
public String getServerKeyStorePath() {
|
||||
return tlsEnabled ? serverKeystorePath.toString() : null;
|
||||
}
|
||||
|
||||
public String getServerKeyStorePassword() {
|
||||
return tlsEnabled ? STORE_PASSWORD : null;
|
||||
}
|
||||
|
||||
public String getServerTrustStorePath() {
|
||||
return mTlsEnabled ? serverTruststorePath.toString() : null;
|
||||
}
|
||||
|
||||
public String getServerTrustStorePassword() {
|
||||
return mTlsEnabled ? STORE_PASSWORD : null;
|
||||
}
|
||||
|
||||
public SSLContext getClientSSLContext() {
|
||||
return clientSslContext;
|
||||
}
|
||||
|
||||
public boolean isTlsEnabled() {
|
||||
return tlsEnabled;
|
||||
}
|
||||
|
||||
public boolean isMTlsEnabled() {
|
||||
return mTlsEnabled;
|
||||
}
|
||||
|
||||
|
||||
private SSLContext createClientSSLContext() {
|
||||
try {
|
||||
return SSLContextBuilder.create()
|
||||
.loadTrustMaterial(clientsTrustStore, null)
|
||||
.build();
|
||||
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
|
||||
throw new ManagedCertificatesException(e);
|
||||
SSLContextBuilder sslContextBuilder = SSLContextBuilder.create()
|
||||
.loadTrustMaterial(clientTrustStore, TrustAllStrategy.INSTANCE);
|
||||
|
||||
if (mTlsEnabled) {
|
||||
sslContextBuilder.loadKeyMaterial(clientKeyStore, STORE_PASSWORD_CHARS);
|
||||
}
|
||||
|
||||
return sslContextBuilder.build();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initServerCerts(KeystoreUtil.KeystoreFormat keystoreFormat) throws ManagedCertificatesException {
|
||||
private void createStores() {
|
||||
try {
|
||||
serverKeyStore = cryptoProvider.getKeyStore(keystoreFormat);
|
||||
clientsTrustStore = cryptoProvider.getKeyStore(keystoreFormat);
|
||||
KeyPair serverKeyPair = generateKeyPair();
|
||||
X509Certificate serverCertificate = generateX509CertificateCertificate(serverKeyPair, "localhost");
|
||||
|
||||
if (Files.exists(serverKeystorePath) && Files.exists(clientsTruststorePath)) {
|
||||
LOGGER.debugv("Existing Server KeyStore files found in {0}", KEYSTORES_DIR);
|
||||
KeyPair clientKeyPair = generateKeyPair();
|
||||
X509Certificate clientCertificate = generateX509CertificateCertificate(clientKeyPair, "myclient");
|
||||
|
||||
loadKeyStore(serverKeyStore, serverKeystorePath, password);
|
||||
loadKeyStore(clientsTrustStore, clientsTruststorePath, password);
|
||||
} else {
|
||||
LOGGER.debugv("Generating Server KeyStore files in {0}", KEYSTORES_DIR);
|
||||
KeyStore serverKeyStore = cryptoProvider.getKeyStore(keystoreFormat);
|
||||
serverKeyStore.load(null, STORE_PASSWORD_CHARS);
|
||||
serverKeyStore.setKeyEntry("server-key", serverKeyPair.getPrivate(), STORE_PASSWORD_CHARS, new X509Certificate[] { serverCertificate });
|
||||
save(serverKeyStore, serverKeystorePath);
|
||||
|
||||
generateKeystoreAndTruststore(serverKeyStore, clientsTrustStore, "localhost");
|
||||
// store the generated keystore and truststore in a temp folder
|
||||
try (FileOutputStream fos = new FileOutputStream(serverKeystorePath.toFile())) {
|
||||
serverKeyStore.store(fos, password);
|
||||
}
|
||||
try (FileOutputStream fos = new FileOutputStream(clientsTruststorePath.toFile())) {
|
||||
clientsTrustStore.store(fos, password);
|
||||
}
|
||||
}
|
||||
KeyStore serverTrustStore = cryptoProvider.getKeyStore(keystoreFormat);
|
||||
serverTrustStore.load(null, STORE_PASSWORD_CHARS);
|
||||
serverTrustStore.setCertificateEntry("myclient-certificate", clientCertificate);
|
||||
save(serverTrustStore, serverTruststorePath);
|
||||
|
||||
clientKeyStore = cryptoProvider.getKeyStore(keystoreFormat);
|
||||
clientKeyStore.load(null, STORE_PASSWORD_CHARS);
|
||||
clientKeyStore.setKeyEntry("client-key", clientKeyPair.getPrivate(), STORE_PASSWORD_CHARS, new X509Certificate[] { clientCertificate });
|
||||
save(clientKeyStore, clientKeystorePath);
|
||||
|
||||
clientTrustStore = cryptoProvider.getKeyStore(keystoreFormat);
|
||||
clientTrustStore.load(null, STORE_PASSWORD_CHARS);
|
||||
clientTrustStore.setCertificateEntry("server-certificate", serverCertificate);
|
||||
save(clientTrustStore, clientTruststorePath);
|
||||
} catch (Exception e) {
|
||||
throw new ManagedCertificatesException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadKeyStore(KeyStore keyStore, Path keyStorePath, char[] keyStorePasswd) throws NoSuchAlgorithmException, IOException, CertificateException {
|
||||
try (FileInputStream fis = new FileInputStream(keyStorePath.toFile())) {
|
||||
keyStore.load(fis, keyStorePasswd);
|
||||
private Path resolvePath(String fileName) {
|
||||
return KEYSTORES_DIR.resolve(fileName + "." + keystoreFormat.getPrimaryExtension());
|
||||
}
|
||||
|
||||
private void save(KeyStore store, Path storePath) throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException {
|
||||
try (FileOutputStream fos = new FileOutputStream(storePath.toFile())) {
|
||||
store.store(fos, STORE_PASSWORD_CHARS);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateKeystoreAndTruststore(KeyStore keyStore, KeyStore trustStore, String subject) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateException, IOException, KeyStoreException, Exception {
|
||||
keyStore.load(null);
|
||||
trustStore.load(null);
|
||||
|
||||
KeyPair keyPair = generateKeyPair();
|
||||
X509Certificate cert = generateX509CertificateCertificate(keyPair, subject);
|
||||
|
||||
keyStore.setCertificateEntry(CERT_ENTRY, cert);
|
||||
trustStore.setCertificateEntry(CERT_ENTRY, cert);
|
||||
keyStore.setKeyEntry(PRV_KEY_ENTRY, keyPair.getPrivate(), password, new X509Certificate[]{cert});
|
||||
private KeyStore load(Path keyStorePath) {
|
||||
try (FileInputStream fis = new FileInputStream(keyStorePath.toFile())) {
|
||||
KeyStore keyStore = cryptoProvider.getKeyStore(keystoreFormat);
|
||||
keyStore.load(fis, STORE_PASSWORD_CHARS);
|
||||
return keyStore;
|
||||
} catch (Exception e) {
|
||||
throw new ManagedCertificatesException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyPair generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException {
|
||||
|
||||
@ -11,6 +11,7 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.testframework.TestFrameworkExecutor;
|
||||
import org.keycloak.testframework.server.KeycloakServer;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.InvocationInterceptor;
|
||||
@ -165,7 +166,7 @@ public class Registry implements ExtensionContext.Store.CloseableResource {
|
||||
}
|
||||
|
||||
Class testClass = testInstance.getClass();
|
||||
RequestedInstance requestedServerInstance = createRequestedInstance(testClass.getAnnotations(), null);
|
||||
RequestedInstance requestedServerInstance = createRequestedInstance(testClass.getAnnotations(), KeycloakServer.class);
|
||||
if (requestedServerInstance != null) {
|
||||
requestedInstances.add(requestedServerInstance);
|
||||
}
|
||||
@ -177,6 +178,17 @@ public class Registry implements ExtensionContext.Store.CloseableResource {
|
||||
}
|
||||
}
|
||||
|
||||
List<RequiredDependencies.RequiredDependency> dependencies = requestedInstances.stream().flatMap(r -> r.getSupplier().getDependencies().getList().stream()).toList();
|
||||
for (RequiredDependencies.RequiredDependency dependency : dependencies) {
|
||||
boolean dependencyRequested = requestedInstances.stream().anyMatch(r -> r.getValueType().equals(dependency.valueType()) && Objects.equals(r.getRef(), dependency.ref()));
|
||||
if (!dependencyRequested) {
|
||||
Supplier<?, ?> supplier = extensions.findSupplierByType(dependency.valueType());
|
||||
Annotation defaultAnnotation = DefaultAnnotationProxy.proxy(supplier.getAnnotationClass(), dependency.ref());
|
||||
RequestedInstance<?, ?> requestDependency = createRequestedInstance(new Annotation[]{ defaultAnnotation }, dependency.valueType());
|
||||
requestedInstances.add(requestDependency);
|
||||
}
|
||||
}
|
||||
|
||||
logger.logRequestedInstances(requestedInstances);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
package org.keycloak.testframework.injection;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class RequiredDependencies {
|
||||
|
||||
private static final RequiredDependencies NONE = new RequiredDependencies();
|
||||
|
||||
private List<RequiredDependency> dependencies;
|
||||
|
||||
public static RequiredDependencies none() {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
public static RequiredDependencies create(Class<?> valueType) {
|
||||
return new RequiredDependencies().add(valueType);
|
||||
}
|
||||
|
||||
public static RequiredDependencies create(Class<?> valueType, String ref) {
|
||||
return new RequiredDependencies().add(valueType, ref);
|
||||
}
|
||||
|
||||
public RequiredDependencies add(Class<?> valueType) {
|
||||
dependencies.add(new RequiredDependency(valueType, null));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RequiredDependencies add(Class<?> valueType, String ref) {
|
||||
dependencies.add(new RequiredDependency(valueType, ref));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RequiredDependencies() {
|
||||
this.dependencies = new LinkedList<>();
|
||||
}
|
||||
|
||||
List<RequiredDependency> getList() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public record RequiredDependency(Class<?> valueType, String ref) {
|
||||
}
|
||||
|
||||
}
|
||||
@ -44,4 +44,8 @@ public interface Supplier<T, S extends Annotation> {
|
||||
return SupplierOrder.DEFAULT;
|
||||
}
|
||||
|
||||
default RequiredDependencies getDependencies() {
|
||||
return RequiredDependencies.none();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -122,6 +122,11 @@ public class UserConfigBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserConfigBuilder serviceAccountId(String serviceAccountClientId) {
|
||||
rep.setServiceAccountClientId(serviceAccountClientId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Best practice is to use other convenience methods when configuring a user, but while the framework is under
|
||||
* active development there may not be a way to perform all updates required. In these cases this method allows
|
||||
|
||||
@ -10,6 +10,7 @@ import org.keycloak.testframework.injection.InstanceContext;
|
||||
import org.keycloak.testframework.injection.LifeCycle;
|
||||
import org.keycloak.testframework.injection.Registry;
|
||||
import org.keycloak.testframework.injection.RequestedInstance;
|
||||
import org.keycloak.testframework.injection.RequiredDependencies;
|
||||
import org.keycloak.testframework.injection.Supplier;
|
||||
import org.keycloak.testframework.injection.SupplierHelpers;
|
||||
import org.keycloak.testframework.injection.SupplierOrder;
|
||||
@ -50,10 +51,17 @@ public abstract class AbstractKeycloakServerSupplier implements Supplier<Keycloa
|
||||
ServerConfigInterceptorHelper interceptor = new ServerConfigInterceptorHelper(instanceContext.getRegistry());
|
||||
command = interceptor.intercept(command, instanceContext);
|
||||
|
||||
if (command.tlsEnabled()) {
|
||||
ManagedCertificates managedCert = instanceContext.getDependency(ManagedCertificates.class);
|
||||
command.option("https-key-store-file", managedCert.getKeycloakServerKeyStorePath());
|
||||
command.option("https-key-store-password", managedCert.getKeycloakServerKeyStorePassword());
|
||||
ManagedCertificates managedCert = instanceContext.getDependency(ManagedCertificates.class);
|
||||
|
||||
if (managedCert.isTlsEnabled()) {
|
||||
command.option("https-key-store-file", managedCert.getServerKeyStorePath());
|
||||
command.option("https-key-store-password", managedCert.getServerKeyStorePassword());
|
||||
}
|
||||
|
||||
if (managedCert.isMTlsEnabled()) {
|
||||
command.option("https-client-auth", "request");
|
||||
command.option("https-trust-store-file", managedCert.getServerTrustStorePath());
|
||||
command.option("https-trust-store-password", managedCert.getServerTrustStorePassword());
|
||||
}
|
||||
|
||||
command.log().fromConfig(Config.getConfig());
|
||||
@ -66,7 +74,7 @@ public abstract class AbstractKeycloakServerSupplier implements Supplier<Keycloa
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
KeycloakServer server = getServer();
|
||||
server.start(command);
|
||||
server.start(command, managedCert.isTlsEnabled());
|
||||
|
||||
getLogger().infov("Keycloak test server started in {0} ms", System.currentTimeMillis() - start);
|
||||
|
||||
@ -88,6 +96,11 @@ public abstract class AbstractKeycloakServerSupplier implements Supplier<Keycloa
|
||||
instanceContext.getValue().stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequiredDependencies getDependencies() {
|
||||
return RequiredDependencies.create(ManagedCertificates.class);
|
||||
}
|
||||
|
||||
public abstract KeycloakServer getServer();
|
||||
|
||||
public abstract boolean requiresDatabase();
|
||||
|
||||
@ -22,15 +22,15 @@ public class DistributionKeycloakServer implements KeycloakServer {
|
||||
private RawKeycloakDistribution keycloak;
|
||||
|
||||
private final boolean debug;
|
||||
private boolean enableTls = false;
|
||||
private boolean tlsEnabled = false;
|
||||
|
||||
public DistributionKeycloakServer(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder) {
|
||||
enableTls = keycloakServerConfigBuilder.tlsEnabled();
|
||||
public void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder, boolean tlsEnabled) {
|
||||
this.tlsEnabled = tlsEnabled;
|
||||
keycloak = new RawKeycloakDistribution(false, MANUAL_STOP, false, RE_CREATE, REMOVE_BUILD_OPTIONS_AFTER_BUILD, REQUEST_PORT, new LoggingOutputConsumer());
|
||||
|
||||
// RawKeycloakDistribution sets "DEBUG_SUSPEND", not "DEBUG" when debug is passed to constructor
|
||||
@ -56,7 +56,7 @@ public class DistributionKeycloakServer implements KeycloakServer {
|
||||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
if (isTlsEnabled()) {
|
||||
if (tlsEnabled) {
|
||||
return "https://localhost:8443";
|
||||
} else {
|
||||
return "http://localhost:8080";
|
||||
@ -65,18 +65,13 @@ public class DistributionKeycloakServer implements KeycloakServer {
|
||||
|
||||
@Override
|
||||
public String getManagementBaseUrl() {
|
||||
if (isTlsEnabled()) {
|
||||
if (tlsEnabled) {
|
||||
return "https://localhost:9000";
|
||||
} else {
|
||||
return "http://localhost:9000";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTlsEnabled() {
|
||||
return enableTls;
|
||||
}
|
||||
|
||||
private static final class LoggingOutputConsumer implements OutputConsumer {
|
||||
|
||||
private static final Pattern LOG_PATTERN = Pattern.compile("([^ ]*) ([^ ]*) ([A-Z]*)([ ]*)(.*)");
|
||||
|
||||
@ -16,12 +16,12 @@ public class EmbeddedKeycloakServer implements KeycloakServer {
|
||||
|
||||
private Keycloak keycloak;
|
||||
private Path homeDir;
|
||||
private boolean enableTls = false;
|
||||
private boolean tlsEnabled = false;
|
||||
|
||||
@Override
|
||||
public void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder) {
|
||||
public void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder, boolean tlsEnabled) {
|
||||
Keycloak.Builder builder = Keycloak.builder().setVersion(Version.VERSION);
|
||||
enableTls = keycloakServerConfigBuilder.tlsEnabled();
|
||||
this.tlsEnabled = tlsEnabled;
|
||||
|
||||
for(Dependency dependency : keycloakServerConfigBuilder.toDependencies()) {
|
||||
builder.addDependency(dependency.getGroupId(), dependency.getArtifactId(), "");
|
||||
@ -64,7 +64,7 @@ public class EmbeddedKeycloakServer implements KeycloakServer {
|
||||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
if (isTlsEnabled()) {
|
||||
if (tlsEnabled) {
|
||||
return "https://localhost:8443";
|
||||
} else {
|
||||
return "http://localhost:8080";
|
||||
@ -73,15 +73,10 @@ public class EmbeddedKeycloakServer implements KeycloakServer {
|
||||
|
||||
@Override
|
||||
public String getManagementBaseUrl() {
|
||||
if (isTlsEnabled()) {
|
||||
if (tlsEnabled) {
|
||||
return "https://localhost:9001";
|
||||
} else {
|
||||
return "http://localhost:9001";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTlsEnabled() {
|
||||
return enableTls;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ package org.keycloak.testframework.server;
|
||||
|
||||
public interface KeycloakServer {
|
||||
|
||||
void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder);
|
||||
void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder, boolean tlsEnabled);
|
||||
|
||||
void stop();
|
||||
|
||||
@ -10,5 +10,4 @@ public interface KeycloakServer {
|
||||
|
||||
String getManagementBaseUrl();
|
||||
|
||||
boolean isTlsEnabled();
|
||||
}
|
||||
|
||||
@ -34,7 +34,6 @@ public class KeycloakServerConfigBuilder {
|
||||
private final Set<Path> configFiles = new HashSet<>();
|
||||
private CacheType cacheType = CacheType.LOCAL;
|
||||
private boolean externalInfinispan = false;
|
||||
private boolean tlsEnabled = false;
|
||||
|
||||
private KeycloakServerConfigBuilder(String command) {
|
||||
this.command = command;
|
||||
@ -107,15 +106,6 @@ public class KeycloakServerConfigBuilder {
|
||||
dependencies.add(new DependencyBuilder().setGroupId(groupId).setArtifactId(artifactId).build());
|
||||
return this;
|
||||
}
|
||||
|
||||
public KeycloakServerConfigBuilder tlsEnabled(boolean enabled) {
|
||||
tlsEnabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean tlsEnabled() {
|
||||
return tlsEnabled ;
|
||||
}
|
||||
|
||||
public KeycloakServerConfigBuilder cacheConfigFile(String resourcePath) {
|
||||
try {
|
||||
|
||||
@ -17,13 +17,13 @@ import static java.lang.System.out;
|
||||
|
||||
public class RemoteKeycloakServer implements KeycloakServer {
|
||||
|
||||
private boolean enableTls = false;
|
||||
private boolean tlsEnabled = false;
|
||||
|
||||
private String kcwCommand;
|
||||
|
||||
@Override
|
||||
public void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder) {
|
||||
enableTls = keycloakServerConfigBuilder.tlsEnabled();
|
||||
public void start(KeycloakServerConfigBuilder keycloakServerConfigBuilder, boolean tlsEnabled) {
|
||||
this.tlsEnabled = tlsEnabled;
|
||||
kcwCommand = Config.getValueTypeConfig(KeycloakServer.class, "kcw", null, String.class);
|
||||
if (!verifyRunningKeycloak()) {
|
||||
if (kcwCommand != null) {
|
||||
@ -41,7 +41,7 @@ public class RemoteKeycloakServer implements KeycloakServer {
|
||||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
if (isTlsEnabled()) {
|
||||
if (tlsEnabled) {
|
||||
return "https://localhost:8443";
|
||||
} else {
|
||||
return "http://localhost:8080";
|
||||
@ -50,18 +50,13 @@ public class RemoteKeycloakServer implements KeycloakServer {
|
||||
|
||||
@Override
|
||||
public String getManagementBaseUrl() {
|
||||
if (isTlsEnabled()) {
|
||||
if (tlsEnabled) {
|
||||
return "https://localhost:9000";
|
||||
} else {
|
||||
return "http://localhost:9000";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTlsEnabled() {
|
||||
return enableTls;
|
||||
}
|
||||
|
||||
private void printStartupInstructionsManual(KeycloakServerConfigBuilder config) {
|
||||
out.println("Remote Keycloak server is not running on " + getBaseUrl() + ", please start Keycloak with:");
|
||||
out.println();
|
||||
|
||||
@ -2,23 +2,18 @@ package org.keycloak.test.examples;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.testframework.annotations.InjectAdminClient;
|
||||
import org.keycloak.testframework.annotations.InjectHttpClient;
|
||||
import org.keycloak.testframework.annotations.InjectKeycloakUrls;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.https.CertificatesConfig;
|
||||
import org.keycloak.testframework.https.CertificatesConfigBuilder;
|
||||
import org.keycloak.testframework.https.InjectCertificates;
|
||||
import org.keycloak.testframework.https.ManagedCertificates;
|
||||
import org.keycloak.testframework.oauth.OAuthClient;
|
||||
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfig;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
|
||||
import org.keycloak.testframework.server.KeycloakUrls;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
@ -27,7 +22,7 @@ import org.apache.http.client.methods.HttpGet;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@KeycloakIntegrationTest(config = TlsEnabledTest.TlsEnabledServerConfig.class)
|
||||
@KeycloakIntegrationTest
|
||||
public class TlsEnabledTest {
|
||||
|
||||
@InjectHttpClient
|
||||
@ -39,32 +34,20 @@ public class TlsEnabledTest {
|
||||
@InjectAdminClient
|
||||
Keycloak adminClient;
|
||||
|
||||
@InjectCertificates
|
||||
@InjectCertificates(config = TlsEnabledConfig.class)
|
||||
ManagedCertificates managedCertificates;
|
||||
|
||||
@InjectKeycloakUrls
|
||||
KeycloakUrls keycloakUrls;
|
||||
|
||||
|
||||
@Test
|
||||
public void testCertSupplier() throws KeyStoreException {
|
||||
public void testCertSupplier() {
|
||||
Assertions.assertNotNull(managedCertificates);
|
||||
|
||||
KeyStore trustStore = managedCertificates.getClientTrustStore();
|
||||
Assertions.assertNotNull(trustStore);
|
||||
Assertions.assertNotNull(managedCertificates.getServerKeyStorePath());
|
||||
Assertions.assertNull(managedCertificates.getServerTrustStorePath());
|
||||
|
||||
X509Certificate cert = managedCertificates.getKeycloakServerCertificate();
|
||||
Assertions.assertNotNull(cert);
|
||||
Assertions.assertEquals(cert.getSerialNumber(), ((X509Certificate) trustStore.getCertificate(ManagedCertificates.CERT_ENTRY)).getSerialNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCertDetails() throws CertificateNotYetValidException, CertificateExpiredException {
|
||||
X509Certificate cert = managedCertificates.getKeycloakServerCertificate();
|
||||
|
||||
cert.checkValidity();
|
||||
Assertions.assertEquals("CN=localhost", cert.getSubjectX500Principal().getName());
|
||||
Assertions.assertEquals("CN=localhost", cert.getIssuerX500Principal().getName());
|
||||
Assertions.assertNotNull(managedCertificates.getClientSSLContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -87,11 +70,10 @@ public class TlsEnabledTest {
|
||||
Assertions.assertTrue(oAuthClient.doWellKnownRequest().getTokenEndpoint().startsWith("https://"));
|
||||
}
|
||||
|
||||
|
||||
public static class TlsEnabledServerConfig implements KeycloakServerConfig {
|
||||
private static class TlsEnabledConfig implements CertificatesConfig {
|
||||
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) {
|
||||
public CertificatesConfigBuilder configure(CertificatesConfigBuilder config) {
|
||||
return config.tlsEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright 2020 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.keycloak.tests.admin;
|
||||
|
||||
import jakarta.ws.rs.NotAuthorizedException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.authentication.authenticators.client.X509ClientAuthenticator;
|
||||
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.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testframework.admin.AdminClientFactory;
|
||||
import org.keycloak.testframework.annotations.InjectAdminClientFactory;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.https.CertificatesConfig;
|
||||
import org.keycloak.testframework.https.CertificatesConfigBuilder;
|
||||
import org.keycloak.testframework.https.InjectCertificates;
|
||||
import org.keycloak.testframework.https.ManagedCertificates;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.RealmConfig;
|
||||
import org.keycloak.testframework.realm.RealmConfigBuilder;
|
||||
import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet;
|
||||
import org.keycloak.testframework.remote.timeoffset.TimeOffSet;
|
||||
import org.keycloak.testframework.util.ApiUtil;
|
||||
import org.keycloak.tests.utils.admin.AdminApiUtil;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test for the various "Advanced" scenarios of java admin-client
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@KeycloakIntegrationTest
|
||||
public class AdminClientTest {
|
||||
|
||||
@InjectRealm(config = TestRealmConfig.class)
|
||||
ManagedRealm testRealm;
|
||||
|
||||
@InjectAdminClientFactory
|
||||
AdminClientFactory adminClientFactory;
|
||||
|
||||
@InjectCertificates(config = MTlsCertificatesEnabled.class)
|
||||
ManagedCertificates managedCertificates;
|
||||
|
||||
@InjectTimeOffSet
|
||||
TimeOffSet timeOffSet;
|
||||
|
||||
private static final String TEST_USER_USERNAME = "test-user@localhost";
|
||||
private static final String TEST_USER_PASSWORD = "password";
|
||||
|
||||
private static final String CLIENT_ID = "service-account-cl";
|
||||
private static final String CLIENT_SECRET = "secret1";
|
||||
|
||||
private static final String X509_CLIENT_ID = "x509-client-sa";
|
||||
|
||||
@Test
|
||||
public void clientCredentialsAuthSuccess() {
|
||||
Keycloak adminClient = adminClientFactory.create()
|
||||
.realm(testRealm.getName())
|
||||
.clientId(CLIENT_ID)
|
||||
.clientSecret(CLIENT_SECRET)
|
||||
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
|
||||
.autoClose()
|
||||
.build();
|
||||
|
||||
// Check possible to load the realm
|
||||
RealmRepresentation realm = adminClient.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.assertEquals(testRealm.getName(), realm.getRealm());
|
||||
|
||||
timeOffSet.set(1000);
|
||||
|
||||
// Check still possible to load the realm after original token expired (admin client should automatically re-authenticate)
|
||||
realm = adminClient.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.assertEquals(testRealm.getName(), realm.getRealm());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientCredentialsClientDisabled() {
|
||||
Keycloak adminClient = adminClientFactory.create()
|
||||
.realm(testRealm.getName())
|
||||
.clientId(CLIENT_ID)
|
||||
.clientSecret(CLIENT_SECRET)
|
||||
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
|
||||
.autoClose()
|
||||
.build();
|
||||
|
||||
// Check possible to load the realm
|
||||
RealmRepresentation realm = adminClient.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.assertEquals(testRealm.getName(), realm.getRealm());
|
||||
|
||||
// Disable client and check it should not be possible to load the realms anymore
|
||||
setClientEnabled(CLIENT_ID, false);
|
||||
|
||||
// Check not possible to invoke anymore
|
||||
try {
|
||||
realm = adminClient.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.fail("Not expected to successfully get realm");
|
||||
} catch (NotAuthorizedException nae) {
|
||||
// Expected
|
||||
} finally {
|
||||
setClientEnabled(CLIENT_ID, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adminAuthCloseUserSession() {
|
||||
UserResource user = AdminApiUtil.findUserByUsernameId(testRealm.admin(), TEST_USER_USERNAME);
|
||||
try(Keycloak keycloak = adminClientFactory.create()
|
||||
.realm(testRealm.getName())
|
||||
.username(TEST_USER_USERNAME)
|
||||
.password(TEST_USER_PASSWORD)
|
||||
.clientId(Constants.ADMIN_CLI_CLIENT_ID)
|
||||
.build()
|
||||
) {
|
||||
|
||||
// Check possible to load the realm
|
||||
RealmRepresentation realm = keycloak.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.assertEquals(testRealm.getName(), realm.getRealm());
|
||||
|
||||
Assertions.assertEquals(1, user.getUserSessions().size());
|
||||
}
|
||||
|
||||
Assertions.assertEquals(0, user.getUserSessions().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adminAuthClientDisabled() {
|
||||
Keycloak adminClient = adminClientFactory.create()
|
||||
.realm(testRealm.getName())
|
||||
.username(TEST_USER_USERNAME)
|
||||
.password(TEST_USER_PASSWORD)
|
||||
.clientId(Constants.ADMIN_CLI_CLIENT_ID)
|
||||
.build();
|
||||
|
||||
// Check possible to load the realm
|
||||
RealmRepresentation realm = adminClient.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.assertEquals(testRealm.getName(), 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(testRealm.getName()).toRepresentation();
|
||||
Assertions.fail("Not expected to successfully get realm");
|
||||
} catch (NotAuthorizedException nae) {
|
||||
// Expected
|
||||
} finally {
|
||||
setClientEnabled(Constants.ADMIN_CLI_CLIENT_ID, true);
|
||||
adminClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adminAuthUserDisabled() {
|
||||
Keycloak adminClient = adminClientFactory.create()
|
||||
.realm(testRealm.getName())
|
||||
.username(TEST_USER_USERNAME)
|
||||
.password(TEST_USER_PASSWORD)
|
||||
.clientId(Constants.ADMIN_CLI_CLIENT_ID)
|
||||
.build();
|
||||
|
||||
Keycloak adminClientOffline = adminClientFactory.create()
|
||||
.realm(testRealm.getName())
|
||||
.username(TEST_USER_USERNAME)
|
||||
.password(TEST_USER_PASSWORD)
|
||||
.clientId(Constants.ADMIN_CLI_CLIENT_ID)
|
||||
.scope(OAuth2Constants.OFFLINE_ACCESS)
|
||||
.build();
|
||||
|
||||
// Check possible to load the realm
|
||||
RealmRepresentation realm = adminClient.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.assertEquals(testRealm.getName(), realm.getRealm());
|
||||
realm = adminClientOffline.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.assertEquals(testRealm.getName(), realm.getRealm());
|
||||
|
||||
// Disable user and check it should not be possible to load the realms anymore
|
||||
setUserEnabled(TEST_USER_USERNAME, false);
|
||||
|
||||
// Check not possible to invoke anymore
|
||||
try {
|
||||
realm = adminClient.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.fail("Not expected to successfully get realm");
|
||||
} catch (NotAuthorizedException nae) {
|
||||
// Expected
|
||||
}
|
||||
try {
|
||||
realm = adminClientOffline.realm(testRealm.getName()).toRepresentation();
|
||||
Assertions.fail("Not expected to successfully get realm");
|
||||
} catch (NotAuthorizedException nae) {
|
||||
// Expected
|
||||
} finally {
|
||||
setUserEnabled(TEST_USER_USERNAME, true);
|
||||
adminClient.close();
|
||||
adminClientOffline.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scopedClientCredentialsAuthSuccess() {
|
||||
// we need to create custom scope after import, otherwise the default scopes are missing.
|
||||
final String scopeName = "myScope";
|
||||
String scopeId = createScope(scopeName, KeycloakModelUtils.generateId());
|
||||
AdminApiUtil.findClientByClientId(testRealm.admin(), CLIENT_ID).addOptionalClientScope(scopeId);
|
||||
|
||||
// with scope
|
||||
try (Keycloak adminClient = adminClientFactory.create()
|
||||
.realm(testRealm.getName())
|
||||
.clientId(CLIENT_ID)
|
||||
.clientSecret(CLIENT_SECRET)
|
||||
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
|
||||
.scope(scopeName)
|
||||
.build()) {
|
||||
final AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
|
||||
Assertions.assertTrue(accessToken.getScope().contains(scopeName));
|
||||
Assertions.assertNotNull(adminClient.realm(testRealm.getName()).clientScopes().get(scopeId).toRepresentation());
|
||||
}
|
||||
// without scope
|
||||
try (Keycloak adminClient = adminClientFactory.create()
|
||||
.realm(testRealm.getName())
|
||||
.clientId(CLIENT_ID)
|
||||
.clientSecret(CLIENT_SECRET)
|
||||
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
|
||||
.build()) {
|
||||
final AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
|
||||
Assertions.assertFalse(accessToken.getScope().contains(scopeName));
|
||||
Assertions.assertNotNull(adminClient.realm(testRealm.getName()).clientScopes().get(scopeId).toRepresentation());
|
||||
}
|
||||
}
|
||||
|
||||
// A client secret is not necessary when authentication is
|
||||
// performed via X.509 authorizer.
|
||||
@Test
|
||||
public void noClientSecretWithClientCredentialsAuthSuccess() {
|
||||
final String scopeName = "dummyScope";
|
||||
String scopeId = createScope(scopeName, KeycloakModelUtils.generateId());
|
||||
testRealm.admin().clients().get(testRealm.admin().clients().findByClientId(X509_CLIENT_ID).get(0).getId()).addOptionalClientScope(scopeId);
|
||||
|
||||
// with scope and no client secret
|
||||
try (Keycloak adminClient = adminClientFactory.create().realm(testRealm.getName()).grantType(OAuth2Constants.CLIENT_CREDENTIALS).clientId(X509_CLIENT_ID).scope(scopeName).build()) {
|
||||
final AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
|
||||
Assertions.assertTrue(accessToken.getScope().contains(scopeName));
|
||||
Assertions.assertNotNull(adminClient.realm(testRealm.getName()).clientScopes().get(scopeId).toRepresentation());
|
||||
}
|
||||
// without scope and no client secret
|
||||
try (Keycloak adminClient = adminClientFactory.create().realm(testRealm.getName()).grantType(OAuth2Constants.CLIENT_CREDENTIALS).clientId(X509_CLIENT_ID).scope(null).build()) {
|
||||
final AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
|
||||
Assertions.assertFalse(accessToken.getScope().contains(scopeName));
|
||||
Assertions.assertNotNull(adminClient.realm(testRealm.getName()).clientScopes().get(scopeId).toRepresentation());
|
||||
}
|
||||
}
|
||||
|
||||
private void setUserEnabled(String username, boolean enabled) {
|
||||
UserResource user = AdminApiUtil.findUserByUsernameId(testRealm.admin(), username);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.setEnabled(enabled);
|
||||
user.update(userRep);
|
||||
}
|
||||
|
||||
private void setClientEnabled(String clientId, boolean enabled) {
|
||||
ClientResource client = AdminApiUtil.findClientByClientId(testRealm.admin(), clientId);
|
||||
ClientRepresentation clientRep = client.toRepresentation();
|
||||
clientRep.setEnabled(enabled);
|
||||
client.update(clientRep);
|
||||
}
|
||||
|
||||
private String createScope(String scopeName, String scopeId) {
|
||||
final ClientScopeRepresentation testScope = new ClientScopeRepresentation();
|
||||
testScope.setId(scopeId);
|
||||
testScope.setName(scopeName);
|
||||
testScope.setProtocol("openid-connect");
|
||||
|
||||
Response response = testRealm.admin().clientScopes().create(testScope);
|
||||
Assertions.assertEquals(201, response.getStatus());
|
||||
return ApiUtil.getCreatedId(response);
|
||||
}
|
||||
|
||||
private static class TestRealmConfig implements RealmConfig {
|
||||
|
||||
@Override
|
||||
public RealmConfigBuilder configure(RealmConfigBuilder realm) {
|
||||
realm.addUser(TEST_USER_USERNAME)
|
||||
.name("test", "user")
|
||||
.email("testuser@localhost.com")
|
||||
.emailVerified(true)
|
||||
.password(TEST_USER_PASSWORD)
|
||||
.clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN)
|
||||
.roles(OAuth2Constants.OFFLINE_ACCESS);
|
||||
|
||||
realm.addClient(CLIENT_ID)
|
||||
.secret(CLIENT_SECRET)
|
||||
.serviceAccountsEnabled(true);
|
||||
|
||||
realm.addUser(ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + CLIENT_ID)
|
||||
.name("serviceAccount", "user")
|
||||
.email("serviceAccountUser@localhost.com")
|
||||
.serviceAccountId(CLIENT_ID)
|
||||
.clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN);
|
||||
|
||||
realm.addClient(X509_CLIENT_ID)
|
||||
.serviceAccountsEnabled(true)
|
||||
.authenticatorType(X509ClientAuthenticator.PROVIDER_ID)
|
||||
.attribute(X509ClientAuthenticator.ATTR_SUBJECT_DN, "(.*?)(?:$)")
|
||||
.attribute(X509ClientAuthenticator.ATTR_ALLOW_REGEX_PATTERN_COMPARISON, "true");
|
||||
|
||||
// This user is associated with the x509-client-sa service account above and
|
||||
// give the service account a service account role "realm-management:realm-admin".
|
||||
// Without the "realm-management:realm-admin" role we won't be able to test any actual
|
||||
// admin call.
|
||||
realm.addUser(ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + X509_CLIENT_ID)
|
||||
.name("x509ServiceAccount", "user")
|
||||
.email("x509ServiceAccountUser@localhost.com")
|
||||
.emailVerified(true)
|
||||
.serviceAccountId(X509_CLIENT_ID)
|
||||
.clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN);
|
||||
|
||||
return realm;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MTlsCertificatesEnabled implements CertificatesConfig {
|
||||
|
||||
@Override
|
||||
public CertificatesConfigBuilder configure(CertificatesConfigBuilder config) {
|
||||
return config.tlsEnabled(true).mTlsEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,7 +42,7 @@ public class FipsNonStrictTestSuite {
|
||||
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) {
|
||||
return config.features(Profile.Feature.FIPS).tlsEnabled(true)
|
||||
return config.features(Profile.Feature.FIPS)
|
||||
.option("fips-mode", "non-strict")
|
||||
.dependency("org.bouncycastle", "bc-fips")
|
||||
.dependency("org.bouncycastle", "bctls-fips")
|
||||
@ -55,7 +55,7 @@ public class FipsNonStrictTestSuite {
|
||||
|
||||
@Override
|
||||
public CertificatesConfigBuilder configure(CertificatesConfigBuilder config) {
|
||||
return config.keystoreFormat(KeystoreUtil.KeystoreFormat.PKCS12);
|
||||
return config.tlsEnabled(true).keystoreFormat(KeystoreUtil.KeystoreFormat.PKCS12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ public class FipsStrictTestSuite {
|
||||
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) {
|
||||
return config.features(Profile.Feature.FIPS).tlsEnabled(true)
|
||||
return config.features(Profile.Feature.FIPS)
|
||||
.option("fips-mode", "strict")
|
||||
.option("spi-password-hashing-pbkdf2-max-padding-length", "14")
|
||||
.option("spi-password-hashing-pbkdf2-sha256-max-padding-length", "14")
|
||||
@ -58,7 +58,7 @@ public class FipsStrictTestSuite {
|
||||
|
||||
@Override
|
||||
public CertificatesConfigBuilder configure(CertificatesConfigBuilder config) {
|
||||
return config.keystoreFormat(KeystoreUtil.KeystoreFormat.BCFKS);
|
||||
return config.tlsEnabled(true).keystoreFormat(KeystoreUtil.KeystoreFormat.BCFKS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ get_default_commit_message() {
|
||||
read_commit_message() {
|
||||
local default_commit_message
|
||||
default_commit_message=$(get_default_commit_message)
|
||||
default_commit_message+=$'\n\n'"Part of: #34494"
|
||||
default_commit_message+=$'\n\n'"Part of: #35040"
|
||||
|
||||
echo ""
|
||||
echo "Provide a different commit message (optional)"
|
||||
|
||||
@ -18,18 +18,14 @@
|
||||
package org.keycloak.testsuite.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.KeycloakBuilder;
|
||||
import org.keycloak.models.Constants;
|
||||
@ -88,46 +84,6 @@ public class AdminClientUtil {
|
||||
.build();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return KeycloakBuilder.builder()
|
||||
.serverUrl(getAuthServerContextRoot() + "/auth")
|
||||
.realm(realmName)
|
||||
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
|
||||
.clientId(clientId)
|
||||
.clientSecret(clientSecret)
|
||||
.resteasyClient(resteasyClient)
|
||||
.scope(scope).build();
|
||||
}
|
||||
|
||||
public static Keycloak createMTlsAdminClientWithClientCredentialsWithoutSecret(
|
||||
final String realmName, String clientId, String scope)
|
||||
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
|
||||
|
||||
boolean ignoreUnknownProperties = false;
|
||||
ResteasyClient resteasyClient = createResteasyClientWithKeystoreAndTruststore();
|
||||
|
||||
return KeycloakBuilder.builder()
|
||||
.serverUrl(getAuthServerContextRoot() + "/auth")
|
||||
.realm(realmName)
|
||||
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
|
||||
.clientId(clientId)
|
||||
.resteasyClient(resteasyClient)
|
||||
.scope(scope).build();
|
||||
}
|
||||
|
||||
public static Keycloak createAdminClient() throws Exception {
|
||||
return createAdminClient(false, getAuthServerContextRoot());
|
||||
}
|
||||
|
||||
public static Keycloak createAdminClient(boolean ignoreUnknownProperties) throws Exception {
|
||||
return createAdminClient(ignoreUnknownProperties, getAuthServerContextRoot());
|
||||
}
|
||||
|
||||
public static ResteasyClient createResteasyClient() {
|
||||
try {
|
||||
return createResteasyClient(false, null);
|
||||
@ -161,30 +117,6 @@ public class AdminClientUtil {
|
||||
return resteasyClientBuilder.build();
|
||||
}
|
||||
|
||||
public static ResteasyClient createResteasyClientWithKeystoreAndTruststore() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
|
||||
ResteasyClientBuilder resteasyClientBuilder = (ResteasyClientBuilder) ResteasyClientBuilder.newBuilder();
|
||||
|
||||
if ("true".equals(System.getProperty("auth.server.ssl.required"))) {
|
||||
File truststore = new File(PROJECT_BUILD_DIRECTORY, "dependency/keystore/keycloak.truststore");
|
||||
try {
|
||||
resteasyClientBuilder.sslContext(getSSLContextWithTruststoreAndKeystore(
|
||||
truststore, "secret",
|
||||
new File(PROJECT_BUILD_DIRECTORY, "dependency/keystore/keycloak.jks"), "secret"));
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
System.setProperty("javax.net.ssl.trustStore", truststore.getAbsolutePath());
|
||||
}
|
||||
|
||||
resteasyClientBuilder
|
||||
.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.WILDCARD)
|
||||
.connectionPoolSize(NUMBER_OF_CONNECTIONS)
|
||||
.httpEngine(getCustomClientHttpEngine(resteasyClientBuilder, 1, null));
|
||||
|
||||
return resteasyClientBuilder.build();
|
||||
}
|
||||
|
||||
private static SSLContext getSSLContextWithTruststore(File file, String password) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
|
||||
if (!file.isFile()) {
|
||||
throw new RuntimeException("Truststore file not found: " + file.getAbsolutePath());
|
||||
@ -209,27 +141,6 @@ public class AdminClientUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static SSLContext getSSLContextWithTruststoreAndKeystore(
|
||||
File trustStore, String truststorePassword, File keystore, String keystorePassword)
|
||||
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException, UnrecoverableKeyException {
|
||||
if (!trustStore.isFile()) {
|
||||
throw new RuntimeException("Truststore file not found: " + trustStore.getAbsolutePath());
|
||||
}
|
||||
if (!keystore.isFile()) {
|
||||
throw new RuntimeException("Keystore file not found: " + keystore.getAbsolutePath());
|
||||
}
|
||||
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(new FileInputStream(keystore), keystorePassword.toCharArray());
|
||||
|
||||
SSLContext theContext = SSLContexts.custom()
|
||||
.setProtocol("TLS")
|
||||
.loadTrustMaterial(trustStore, truststorePassword == null ? null : truststorePassword.toCharArray())
|
||||
.loadKeyMaterial(ks, keystorePassword.toCharArray())
|
||||
.build();
|
||||
return theContext;
|
||||
}
|
||||
|
||||
public static ClientHttpEngine getCustomClientHttpEngine(ResteasyClientBuilder resteasyClientBuilder, int validateAfterInactivity, Boolean followRedirects) {
|
||||
return new CustomClientHttpEngineBuilder43(validateAfterInactivity, followRedirects).resteasyClientBuilder(resteasyClientBuilder).build();
|
||||
}
|
||||
|
||||
@ -1,329 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.admin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import jakarta.ws.rs.NotAuthorizedException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
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.authentication.authenticators.client.X509ClientAuthenticator;
|
||||
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.representations.idm.UserRepresentation;
|
||||
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.ServerURLs;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for the various "Advanced" scenarios of java admin-client
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
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;
|
||||
|
||||
private static String x509ClientUUID;
|
||||
private static String x509ClientId;
|
||||
|
||||
private static String x509UserName;
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@Override
|
||||
public void beforeAbstractKeycloakTest() throws Exception {
|
||||
super.beforeAbstractKeycloakTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
realmName = "test";
|
||||
RealmBuilder realm = RealmBuilder.create().name(realmName)
|
||||
.testEventListener();
|
||||
|
||||
clientId = "service-account-cl";
|
||||
clientSecret = "secret1";
|
||||
ClientRepresentation enabledAppWithSkipRefreshToken = ClientBuilder.create()
|
||||
.clientId(clientId)
|
||||
.secret(clientSecret)
|
||||
.serviceAccountsEnabled(true)
|
||||
.build();
|
||||
realm.client(enabledAppWithSkipRefreshToken);
|
||||
|
||||
x509ClientId = "x509-client-sa";
|
||||
ClientRepresentation x509ServiceAccountClient = ClientBuilder.create()
|
||||
.clientId(x509ClientId)
|
||||
.serviceAccountsEnabled(true)
|
||||
.build();
|
||||
x509ServiceAccountClient.setClientAuthenticatorType(X509ClientAuthenticator.PROVIDER_ID);
|
||||
x509ServiceAccountClient.setAttributes(Map.of(
|
||||
X509ClientAuthenticator.ATTR_SUBJECT_DN, "(.*?)(?:$)",
|
||||
X509ClientAuthenticator.ATTR_ALLOW_REGEX_PATTERN_COMPARISON, "true"));
|
||||
realm.client(x509ServiceAccountClient);
|
||||
|
||||
userId = KeycloakModelUtils.generateId();
|
||||
userName = ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + enabledAppWithSkipRefreshToken.getClientId();
|
||||
UserBuilder serviceAccountUser = UserBuilder.create()
|
||||
.username(userName)
|
||||
.serviceAccountId(enabledAppWithSkipRefreshToken.getClientId())
|
||||
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN);
|
||||
realm.user(serviceAccountUser);
|
||||
|
||||
// This user is associated with the x509-client-sa service account above and
|
||||
// give the service account a service account role "realm-management:realm-admin".
|
||||
// Without the "realm-management:realm-admin" role we won't be able to test any actual
|
||||
// admin call.
|
||||
x509UserName = ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + x509ServiceAccountClient.getClientId();
|
||||
UserBuilder x509ServiceAccountUser = UserBuilder.create()
|
||||
.username(x509UserName)
|
||||
.serviceAccountId(x509ServiceAccountClient.getClientId())
|
||||
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN);
|
||||
realm.user(x509ServiceAccountUser);
|
||||
|
||||
UserBuilder defaultUser = UserBuilder.create()
|
||||
.id(KeycloakModelUtils.generateId())
|
||||
.username("test-user@localhost")
|
||||
.password("password")
|
||||
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN)
|
||||
.addRoles(OAuth2Constants.OFFLINE_ACCESS);
|
||||
realm.user(defaultUser);
|
||||
|
||||
testRealms.add(realm.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importRealm(RealmRepresentation realm) {
|
||||
super.importRealm(realm);
|
||||
if (Objects.equals(realm.getRealm(), realmName)) {
|
||||
x509ClientUUID = adminClient.realm(realmName).clients().findByClientId(x509ClientId).get(0).getId();
|
||||
clientUUID = adminClient.realm(realmName).clients().findByClientId(clientId).get(0).getId();
|
||||
userId = adminClient.realm(realmName).users().searchByUsername(userName, true).get(0).getId();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientCredentialsAuthSuccess() throws Exception {
|
||||
try (Keycloak adminClient = AdminClientUtil.createAdminClientWithClientCredentials(realmName, clientId, clientSecret, null)) {
|
||||
// Check possible to load the realm
|
||||
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(realmName).toRepresentation();
|
||||
Assert.assertEquals(realmName, realm.getRealm());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientCredentialsClientDisabled() throws Exception {
|
||||
try (Keycloak adminClient = AdminClientUtil.createAdminClientWithClientCredentials(realmName, clientId, clientSecret, null)) {
|
||||
// Check possible to load the realm
|
||||
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(clientId, false);
|
||||
|
||||
// Check not possible to invoke anymore
|
||||
try {
|
||||
realm = adminClient.realm(realmName).toRepresentation();
|
||||
Assert.fail("Not expected to successfully get realm");
|
||||
} catch (NotAuthorizedException nae) {
|
||||
// Expected
|
||||
}
|
||||
} finally {
|
||||
setClientEnabled(clientId, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adminAuthCloseUserSession() throws Exception {
|
||||
UserResource user = ApiUtil.findUserByUsernameId(adminClient.realm(realmName), "test-user@localhost");
|
||||
try (Keycloak keycloak = AdminClientUtil.createAdminClient(false, realmName, "test-user@localhost", "password", Constants.ADMIN_CLI_CLIENT_ID, null)) {
|
||||
// Check possible to load the realm
|
||||
RealmRepresentation realm = keycloak.realm(realmName).toRepresentation();
|
||||
Assert.assertEquals(realmName, realm.getRealm());
|
||||
|
||||
Assert.assertEquals(1, user.getUserSessions().size());
|
||||
}
|
||||
Assert.assertEquals(0, user.getUserSessions().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adminAuthClientDisabled() throws Exception {
|
||||
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(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(realmName).toRepresentation();
|
||||
Assert.fail("Not expected to successfully get realm");
|
||||
} catch (NotAuthorizedException nae) {
|
||||
// Expected
|
||||
}
|
||||
} finally {
|
||||
setClientEnabled(Constants.ADMIN_CLI_CLIENT_ID, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adminAuthUserDisabled() throws Exception {
|
||||
try (Keycloak adminClient = AdminClientUtil.createAdminClient(false, realmName, "test-user@localhost", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
|
||||
Keycloak adminClientOffline = AdminClientUtil.createAdminClient(false, ServerURLs.getAuthServerContextRoot(), realmName, "test-user@localhost", "password", Constants.ADMIN_CLI_CLIENT_ID, null, OAuth2Constants.OFFLINE_ACCESS, false);
|
||||
) {
|
||||
// Check possible to load the realm
|
||||
RealmRepresentation realm = adminClient.realm(realmName).toRepresentation();
|
||||
Assert.assertEquals(realmName, realm.getRealm());
|
||||
realm = adminClientOffline.realm(realmName).toRepresentation();
|
||||
Assert.assertEquals(realmName, realm.getRealm());
|
||||
|
||||
// Disable client and check it should not be possible to load the realms anymore
|
||||
setUserEnabled("test-user@localhost", false);
|
||||
|
||||
// Check not possible to invoke anymore
|
||||
try {
|
||||
realm = adminClient.realm(realmName).toRepresentation();
|
||||
Assert.fail("Not expected to successfully get realm");
|
||||
} catch (NotAuthorizedException nae) {
|
||||
// Expected
|
||||
}
|
||||
try {
|
||||
realm = adminClientOffline.realm(realmName).toRepresentation();
|
||||
Assert.fail("Not expected to successfully get realm");
|
||||
} catch (NotAuthorizedException nae) {
|
||||
// Expected
|
||||
}
|
||||
} finally {
|
||||
setUserEnabled("test-user@localhost", true);
|
||||
}
|
||||
}
|
||||
|
||||
@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";
|
||||
String scopeId = createScope(testRealm, scopeName, KeycloakModelUtils.generateId());
|
||||
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));
|
||||
Assert.assertNotNull(adminClient.realm(realmName).clientScopes().get(scopeId).toRepresentation());
|
||||
}
|
||||
// without scope
|
||||
try (Keycloak adminClient = AdminClientUtil.createAdminClientWithClientCredentials(realmName,
|
||||
clientId, clientSecret, null)) {
|
||||
final AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
|
||||
Assert.assertFalse(accessToken.getScope().contains(scopeName));
|
||||
Assert.assertNotNull(adminClient.realm(realmName).clientScopes().get(scopeId).toRepresentation());
|
||||
}
|
||||
}
|
||||
|
||||
// A client secret in not necessary when authentication is
|
||||
// performed via X.509 authorizer.
|
||||
@Test
|
||||
public void noClientSecretWithClientCredentialsAuthSuccess() throws Exception {
|
||||
final RealmResource testRealm = adminClient.realm(realmName);
|
||||
|
||||
final String scopeName = "dummyScope";
|
||||
String scopeId = createScope(testRealm, scopeName, KeycloakModelUtils.generateId());
|
||||
testRealm.clients().get(x509ClientUUID).addOptionalClientScope(scopeId);
|
||||
|
||||
// with scope and no client secret
|
||||
try (Keycloak adminClient = AdminClientUtil.
|
||||
createMTlsAdminClientWithClientCredentialsWithoutSecret(realmName, x509ClientId, scopeName)) {
|
||||
final AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
|
||||
Assert.assertTrue(accessToken.getScope().contains(scopeName));
|
||||
Assert.assertNotNull(adminClient.realm(realmName).clientScopes().get(scopeId).toRepresentation());
|
||||
}
|
||||
// without scope and no client secret
|
||||
try (Keycloak adminClient = AdminClientUtil.
|
||||
createMTlsAdminClientWithClientCredentialsWithoutSecret(realmName, x509ClientId, null)) {
|
||||
final AccessTokenResponse accessToken = adminClient.tokenManager().getAccessToken();
|
||||
Assert.assertFalse(accessToken.getScope().contains(scopeName));
|
||||
Assert.assertNotNull(adminClient.realm(realmName).clientScopes().get(scopeId).toRepresentation());
|
||||
}
|
||||
}
|
||||
|
||||
private void setClientEnabled(String clientId, boolean enabled) {
|
||||
ClientResource client = ApiUtil.findClientByClientId(adminClient.realms().realm(realmName), clientId);
|
||||
ClientRepresentation clientRep = client.toRepresentation();
|
||||
clientRep.setEnabled(enabled);
|
||||
client.update(clientRep);
|
||||
}
|
||||
|
||||
private void setUserEnabled(String username, boolean enabled) {
|
||||
UserResource user = ApiUtil.findUserByUsernameId(adminClient.realms().realm(realmName), username);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.setEnabled(enabled);
|
||||
user.update(userRep);
|
||||
}
|
||||
|
||||
private String createScope(RealmResource testRealm, String scopeName, String scopeId) {
|
||||
final ClientScopeRepresentation testScope =
|
||||
ClientScopeBuilder.create().name(scopeName).protocol("openid-connect").build();
|
||||
testScope.setId(scopeId);
|
||||
try (Response response = testRealm.clientScopes().create(testScope)) {
|
||||
Assert.assertEquals(201, response.getStatus());
|
||||
return ApiUtil.getCreatedId(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
account,4
|
||||
actions,1
|
||||
adapter,IGNORED
|
||||
admin,1
|
||||
authz,3
|
||||
broker,2
|
||||
cli,4
|
||||
|
||||
@ -15,6 +15,5 @@ SamlClientTest
|
||||
UserProfileTest
|
||||
OidcAdvancedClaimToGroupMapperTest
|
||||
OidcAdvancedClaimToRoleMapperTest
|
||||
org.keycloak.testsuite.admin.**
|
||||
org.keycloak.testsuite.authz.**ManagementTest
|
||||
org.keycloak.testsuite.organization.admin.**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user