parent
909740ca51
commit
f3c3bb5001
8 changed files with 91 additions and 45 deletions
|
@ -78,19 +78,6 @@
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-smallrye-metrics-deployment</artifactId>
|
<artifactId>quarkus-smallrye-metrics-deployment</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Cross-DC and ISPN client dependencies -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-infinispan-client-deployment</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.wildfly.security</groupId>
|
|
||||||
<artifactId>wildfly-elytron-sasl-gssapi</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.wildfly.security</groupId>
|
|
||||||
<artifactId>wildfly-elytron-sasl-gs2</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkiverse.vault</groupId>
|
<groupId>io.quarkiverse.vault</groupId>
|
||||||
<artifactId>quarkus-vault-deployment</artifactId>
|
<artifactId>quarkus-vault-deployment</artifactId>
|
||||||
|
|
|
@ -80,8 +80,8 @@
|
||||||
<artifactId>quarkus-smallrye-metrics</artifactId>
|
<artifactId>quarkus-smallrye-metrics</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>org.wildfly.security</groupId>
|
||||||
<artifactId>quarkus-infinispan-client</artifactId>
|
<artifactId>wildfly-elytron</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkiverse.vault</groupId>
|
<groupId>io.quarkiverse.vault</groupId>
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.quarkus.runtime;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.sql.Driver;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
|
||||||
|
public class KeycloakClassLoader extends ClassLoader {
|
||||||
|
|
||||||
|
KeycloakClassLoader() {
|
||||||
|
super(Thread.currentThread().getContextClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<URL> getResources(String name) throws IOException {
|
||||||
|
// drivers are going to be loaded lazily, and we avoid loading all available drivers
|
||||||
|
// see https://github.com/quarkusio/quarkus/pull/7089
|
||||||
|
if (name.contains(Driver.class.getName())) {
|
||||||
|
return Collections.emptyEnumeration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getResources(name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,8 +50,6 @@ import io.quarkus.runtime.annotations.QuarkusMain;
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class KeycloakMain implements QuarkusApplication {
|
public class KeycloakMain implements QuarkusApplication {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(KeycloakMain.class);
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.setProperty("kc.version", Version.VERSION_KEYCLOAK);
|
System.setProperty("kc.version", Version.VERSION_KEYCLOAK);
|
||||||
List<String> cliArgs = Picocli.parseArgs(args);
|
List<String> cliArgs = Picocli.parseArgs(args);
|
||||||
|
@ -80,7 +78,11 @@ public class KeycloakMain implements QuarkusApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void start(ExecutionExceptionHandler errorHandler, PrintWriter errStream) {
|
public static void start(ExecutionExceptionHandler errorHandler, PrintWriter errStream) {
|
||||||
|
ClassLoader originalCl = Thread.currentThread().getContextClassLoader();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
Thread.currentThread().setContextClassLoader(new KeycloakClassLoader());
|
||||||
|
|
||||||
Quarkus.run(KeycloakMain.class, (exitCode, cause) -> {
|
Quarkus.run(KeycloakMain.class, (exitCode, cause) -> {
|
||||||
if (cause != null) {
|
if (cause != null) {
|
||||||
errorHandler.error(errStream,
|
errorHandler.error(errStream,
|
||||||
|
@ -98,6 +100,8 @@ public class KeycloakMain implements QuarkusApplication {
|
||||||
errorHandler.error(errStream,
|
errorHandler.error(errStream,
|
||||||
String.format("Unexpected error when starting the server in (%s) mode", getKeycloakModeFromProfile(getProfileOrDefault("prod"))),
|
String.format("Unexpected error when starting the server in (%s) mode", getKeycloakModeFromProfile(getProfileOrDefault("prod"))),
|
||||||
cause.getCause());
|
cause.getCause());
|
||||||
|
} finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(originalCl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +111,7 @@ public class KeycloakMain implements QuarkusApplication {
|
||||||
@Override
|
@Override
|
||||||
public int run(String... args) throws Exception {
|
public int run(String... args) throws Exception {
|
||||||
if (isDevProfile()) {
|
if (isDevProfile()) {
|
||||||
LOGGER.warnf("Running the server in development mode. DO NOT use this configuration in production.");
|
Logger.getLogger(KeycloakMain.class).warnf("Running the server in development mode. DO NOT use this configuration in production.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int exitCode = ApplicationLifecycleManager.getExitCode();
|
int exitCode = ApplicationLifecycleManager.getExitCode();
|
||||||
|
|
|
@ -62,17 +62,7 @@ public class KeycloakRecorder {
|
||||||
|
|
||||||
public RuntimeValue<CacheManagerFactory> createCacheInitializer(String config, ShutdownContext shutdownContext) {
|
public RuntimeValue<CacheManagerFactory> createCacheInitializer(String config, ShutdownContext shutdownContext) {
|
||||||
try {
|
try {
|
||||||
ConfigurationBuilderHolder builder = new ParserRegistry().parse(config);
|
CacheManagerFactory cacheManagerFactory = new CacheManagerFactory(config);
|
||||||
|
|
||||||
if (builder.getNamedConfigurationBuilders().get("sessions").clustering().cacheMode().isClustered()) {
|
|
||||||
configureTransportStack(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For Infinispan 10, we go with the JBoss marshalling.
|
|
||||||
// TODO: This should be replaced later with the marshalling recommended by infinispan. Probably protostream.
|
|
||||||
// See https://infinispan.org/docs/stable/titles/developing/developing.html#marshalling for the details
|
|
||||||
builder.getGlobalConfigurationBuilder().serialization().marshaller(new JBossUserMarshaller());
|
|
||||||
CacheManagerFactory cacheManagerFactory = new CacheManagerFactory(builder);
|
|
||||||
|
|
||||||
shutdownContext.addShutdownTask(new Runnable() {
|
shutdownContext.addShutdownTask(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,15 +81,6 @@ public class KeycloakRecorder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureTransportStack(ConfigurationBuilderHolder builder) {
|
|
||||||
String transportStack = Configuration.getRawValue("kc.cache-stack");
|
|
||||||
|
|
||||||
if (transportStack != null) {
|
|
||||||
builder.getGlobalConfigurationBuilder().transport().defaultTransport()
|
|
||||||
.addProperty("configurationFile", "default-configs/default-jgroups-" + transportStack + ".xml");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerShutdownHook(ShutdownContext shutdownContext) {
|
public void registerShutdownHook(ShutdownContext shutdownContext) {
|
||||||
shutdownContext.addShutdownTask(new Runnable() {
|
shutdownContext.addShutdownTask(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -122,9 +122,11 @@ public final class QuarkusJpaConnectionProviderFactory implements JpaConnectionP
|
||||||
try {
|
try {
|
||||||
Map<String, Object> unitProperties = emf.getProperties();
|
Map<String, Object> unitProperties = emf.getProperties();
|
||||||
|
|
||||||
unitProperties.entrySet().stream()
|
for (Map.Entry<String, Object> entry : unitProperties.entrySet()) {
|
||||||
.filter(entry -> entry.getKey().startsWith(QUERY_PROPERTY_PREFIX))
|
if (entry.getKey().startsWith(QUERY_PROPERTY_PREFIX)) {
|
||||||
.forEach(entry -> configureNamedQuery(entry.getKey().substring(QUERY_PROPERTY_PREFIX.length()), entry.getValue().toString(), em));
|
configureNamedQuery(entry.getKey().substring(QUERY_PROPERTY_PREFIX.length()), entry.getValue().toString(), em);
|
||||||
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
JpaUtils.closeEntityManager(em);
|
JpaUtils.closeEntityManager(em);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,18 +23,21 @@ import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
|
import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
|
||||||
|
import org.infinispan.configuration.parsing.ParserRegistry;
|
||||||
|
import org.infinispan.jboss.marshalling.core.JBossUserMarshaller;
|
||||||
import org.infinispan.manager.DefaultCacheManager;
|
import org.infinispan.manager.DefaultCacheManager;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
|
|
||||||
public class CacheManagerFactory {
|
public class CacheManagerFactory {
|
||||||
|
|
||||||
private ConfigurationBuilderHolder config;
|
private String config;
|
||||||
private DefaultCacheManager cacheManager;
|
private DefaultCacheManager cacheManager;
|
||||||
private Future<DefaultCacheManager> cacheManagerFuture;
|
private Future<DefaultCacheManager> cacheManagerFuture;
|
||||||
private ExecutorService executor;
|
private ExecutorService executor;
|
||||||
private boolean initialized;
|
private boolean initialized;
|
||||||
|
|
||||||
public CacheManagerFactory(ConfigurationBuilderHolder config) {
|
public CacheManagerFactory(String config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.executor = createThreadPool();
|
this.executor = createThreadPool();
|
||||||
this.cacheManagerFuture = executor.submit(this::startCacheManager);
|
this.cacheManagerFuture = executor.submit(this::startCacheManager);
|
||||||
|
@ -69,7 +72,18 @@ public class CacheManagerFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
private DefaultCacheManager startCacheManager() {
|
private DefaultCacheManager startCacheManager() {
|
||||||
return new DefaultCacheManager(config, isStartEagerly());
|
ConfigurationBuilderHolder builder = new ParserRegistry().parse(config);
|
||||||
|
|
||||||
|
if (builder.getNamedConfigurationBuilders().get("sessions").clustering().cacheMode().isClustered()) {
|
||||||
|
configureTransportStack(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Infinispan 10, we go with the JBoss marshalling.
|
||||||
|
// TODO: This should be replaced later with the marshalling recommended by infinispan. Probably protostream.
|
||||||
|
// See https://infinispan.org/docs/stable/titles/developing/developing.html#marshalling for the details
|
||||||
|
builder.getGlobalConfigurationBuilder().serialization().marshaller(new JBossUserMarshaller());
|
||||||
|
|
||||||
|
return new DefaultCacheManager(builder, isStartEagerly());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isStartEagerly() {
|
private boolean isStartEagerly() {
|
||||||
|
@ -101,4 +115,13 @@ public class CacheManagerFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void configureTransportStack(ConfigurationBuilderHolder builder) {
|
||||||
|
String transportStack = Configuration.getRawValue("kc.cache-stack");
|
||||||
|
|
||||||
|
if (transportStack != null) {
|
||||||
|
builder.getGlobalConfigurationBuilder().transport().defaultTransport()
|
||||||
|
.addProperty("configurationFile", "default-configs/default-jgroups-" + transportStack + ".xml");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,23 +16,30 @@
|
||||||
|
|
||||||
package org.keycloak.credential;
|
package org.keycloak.credential;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
|
||||||
import com.webauthn4j.converter.util.ObjectConverter;
|
import com.webauthn4j.converter.util.ObjectConverter;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
|
|
||||||
public class WebAuthnCredentialProviderFactory implements CredentialProviderFactory<WebAuthnCredentialProvider>, EnvironmentDependentProviderFactory {
|
public class WebAuthnCredentialProviderFactory implements CredentialProviderFactory<WebAuthnCredentialProvider>, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
public static final String PROVIDER_ID = "keycloak-webauthn";
|
public static final String PROVIDER_ID = "keycloak-webauthn";
|
||||||
|
|
||||||
private static ObjectConverter converter = new ObjectConverter();
|
private ObjectConverter converter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CredentialProvider create(KeycloakSession session) {
|
public CredentialProvider create(KeycloakSession session) {
|
||||||
return new WebAuthnCredentialProvider(session, converter);
|
return new WebAuthnCredentialProvider(session, converter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
converter = new ObjectConverter();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
Loading…
Reference in a new issue