fix: remove the reliance on allowed classes (#27368)

closes: #25038

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2024-03-04 07:17:53 -05:00 committed by GitHub
parent a4a6b4f015
commit be3e2fabc4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 212 additions and 21 deletions

View file

@ -102,7 +102,10 @@ import org.keycloak.representations.provider.ScriptProviderDescriptor;
import org.keycloak.representations.provider.ScriptProviderMetadata; import org.keycloak.representations.provider.ScriptProviderMetadata;
import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.userprofile.config.UPConfig;
import org.keycloak.services.ServicesLogger; import org.keycloak.services.ServicesLogger;
import org.keycloak.services.resources.JsResource;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.resources.LoadBalancerResource;
import org.keycloak.services.resources.admin.AdminRoot;
import org.keycloak.theme.ClasspathThemeProviderFactory; import org.keycloak.theme.ClasspathThemeProviderFactory;
import org.keycloak.theme.ClasspathThemeResourceProviderFactory; import org.keycloak.theme.ClasspathThemeResourceProviderFactory;
import org.keycloak.theme.FolderThemeProviderFactory; import org.keycloak.theme.FolderThemeProviderFactory;
@ -640,6 +643,21 @@ class KeycloakProcessor {
buildTimeConditionBuildItemBuildProducer.produce(new BuildTimeConditionBuildItem(index.getIndex().getClassByName(DotName.createSimple( buildTimeConditionBuildItemBuildProducer.produce(new BuildTimeConditionBuildItem(index.getIndex().getClassByName(DotName.createSimple(
KeycloakApplication.class.getName())), false)); KeycloakApplication.class.getName())), false));
if (!Profile.isFeatureEnabled(Profile.Feature.ADMIN_API)) {
buildTimeConditionBuildItemBuildProducer.produce(new BuildTimeConditionBuildItem(index.getIndex().getClassByName(DotName.createSimple(
AdminRoot.class.getName())), false));
}
if (!Profile.isFeatureEnabled(Profile.Feature.JS_ADAPTER)) {
buildTimeConditionBuildItemBuildProducer.produce(new BuildTimeConditionBuildItem(index.getIndex().getClassByName(DotName.createSimple(
JsResource.class.getName())), false));
}
if (!Profile.isFeatureEnabled(Profile.Feature.MULTI_SITE)) {
buildTimeConditionBuildItemBuildProducer.produce(new BuildTimeConditionBuildItem(index.getIndex().getClassByName(DotName.createSimple(
LoadBalancerResource.class.getName())), false));
}
KeycloakHandlerChainCustomizer chainCustomizer = new KeycloakHandlerChainCustomizer(); KeycloakHandlerChainCustomizer chainCustomizer = new KeycloakHandlerChainCustomizer();
scanner.produce(new MethodScannerBuildItem(new MethodScanner() { scanner.produce(new MethodScannerBuildItem(new MethodScanner() {

View file

@ -17,23 +17,20 @@
package org.keycloak.quarkus.runtime.integration.jaxrs; package org.keycloak.quarkus.runtime.integration.jaxrs;
import java.util.HashSet; import io.quarkus.runtime.ShutdownEvent;
import java.util.Set; import io.quarkus.runtime.StartupEvent;
import io.smallrye.common.annotation.Blocking;
import jakarta.enterprise.event.Observes;
import jakarta.ws.rs.ApplicationPath;
import org.keycloak.config.HostnameOptions;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.platform.Platform; import org.keycloak.platform.Platform;
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
import org.keycloak.quarkus.runtime.integration.QuarkusPlatform; import org.keycloak.quarkus.runtime.integration.QuarkusPlatform;
import org.keycloak.quarkus.runtime.services.resources.DebugHostnameSettingsResource;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
import io.quarkus.runtime.ShutdownEvent; import java.util.Set;
import io.quarkus.runtime.StartupEvent;
import io.smallrye.common.annotation.Blocking; import jakarta.enterprise.event.Observes;
import jakarta.ws.rs.ApplicationPath;
@ApplicationPath("/") @ApplicationPath("/")
@Blocking @Blocking
@ -69,13 +66,6 @@ public class QuarkusKeycloakApplication extends KeycloakApplication {
@Override @Override
public Set<Class<?>> getClasses() { public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>(super.getClasses()); return Set.of();
classes.add(QuarkusObjectMapperResolver.class);
classes.add(CloseSessionHandler.class);
classes.add(DebugHostnameSettingsResource.class);
return classes;
} }
} }

View file

@ -37,6 +37,8 @@ import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.HashMap; import java.util.HashMap;
@ -44,6 +46,7 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
@Provider
@Path("/realms") @Path("/realms")
@EndpointDisabled(name = "kc.hostname-debug", stringValue = "false", disableIfMissing = true) @EndpointDisabled(name = "kc.hostname-debug", stringValue = "false", disableIfMissing = true)
public class DebugHostnameSettingsResource { public class DebugHostnameSettingsResource {

View file

@ -0,0 +1,68 @@
/*
* Copyright 2024 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.it.jaxrs.filter;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.net.SocketAddress;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import java.io.IOException;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.PreMatching;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.ext.Provider;
@Provider
@PreMatching
public class TestFilter implements ContainerRequestFilter {
private static final Logger LOG = Logger.getLogger(TestFilter.class);
@Context
UriInfo info;
@Context
HttpServerRequest request;
@Context
KeycloakSession session;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
final String method = requestContext.getMethod();
final String path = info.getPath();
final SocketAddress address = request.remoteAddress();
KeycloakSession s = null;
try {
if (session != null) {
session.getContext();
}
s = session;
} catch (RuntimeException e) {
// should say something like Normal scoped producer method may not return null: org.keycloak.quarkus.runtime.integration.cdi.KeycloakBeanProducer.getKeycloakSession()
}
LOG.infof("Request %s %s has context request %s has keycloaksession %s", method, path, address != null, s != null);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2024 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.it.jaxrs.filter;
import org.keycloak.it.TestProvider;
public class TestFilterTestProvider implements TestProvider {
@Override
public Class<?>[] getClasses() {
return new Class[] {TestFilter.class};
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright 2024 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.it.cli.dist;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test;
import org.keycloak.it.jaxrs.filter.TestFilterTestProvider;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.TestProvider;
import org.keycloak.it.utils.KeycloakDistribution;
import java.util.concurrent.TimeUnit;
import static io.restassured.RestAssured.when;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DistributionTest(keepAlive = true)
@RawDistOnly(reason = "Containers are immutable")
public class JaxRsDistTest {
@Test
@TestProvider(TestFilterTestProvider.class)
public void requestFilterTest(KeycloakDistribution dist) {
CLIResult cliResult = dist.run("start-dev");
cliResult.assertStartedDevMode();
assertEquals(200, when().get("/").getStatusCode());
Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(
() -> cliResult.assertMessage("Request GET / has context request true has keycloaksession false"));
}
}

View file

@ -45,5 +45,7 @@ public interface TestProvider {
* name of the manifest resource that should be created in the provider JAR file. * name of the manifest resource that should be created in the provider JAR file.
* @return * @return
*/ */
Map<String, String> getManifestResources(); default Map<String, String> getManifestResources() {
return Map.of();
}
} }

View file

@ -24,6 +24,8 @@ import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
/** /**
@ -31,6 +33,7 @@ import org.keycloak.models.KeycloakSession;
* *
* <code>org.jboss.resteasy.plugins.providers.jackson.UnrecognizedPropertyExceptionHandler</code> * <code>org.jboss.resteasy.plugins.providers.jackson.UnrecognizedPropertyExceptionHandler</code>
*/ */
@Provider
public class KcUnrecognizedPropertyExceptionHandler implements ExceptionMapper<UnrecognizedPropertyException> { public class KcUnrecognizedPropertyExceptionHandler implements ExceptionMapper<UnrecognizedPropertyException> {
@Context @Context

View file

@ -32,6 +32,8 @@ import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
@ -40,6 +42,7 @@ import java.util.Properties;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@Provider
public class KeycloakErrorHandler implements ExceptionMapper<Throwable> { public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
private static final Logger logger = Logger.getLogger(KeycloakErrorHandler.class); private static final Logger logger = Logger.getLogger(KeycloakErrorHandler.class);

View file

@ -20,16 +20,17 @@
package org.keycloak.services.error; package org.keycloak.services.error;
import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
/** /**
* Override explicitly added ExceptionMapper for handling {@link MismatchedInputException} in RestEasy Jackson * Override explicitly added ExceptionMapper for handling {@link MismatchedInputException} in RestEasy Jackson
*/ */
@Provider
public class KeycloakMismatchedInputExceptionHandler implements ExceptionMapper<MismatchedInputException> { public class KeycloakMismatchedInputExceptionHandler implements ExceptionMapper<MismatchedInputException> {
@Context @Context

View file

@ -26,10 +26,12 @@ import jakarta.annotation.Priority;
import jakarta.ws.rs.container.ContainerResponseContext; import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter; import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.container.PreMatching; import jakarta.ws.rs.container.PreMatching;
import jakarta.ws.rs.ext.Provider;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
@Provider
@PreMatching @PreMatching
@Priority(10) @Priority(10)
public class KeycloakSecurityHeadersFilter implements ContainerResponseFilter { public class KeycloakSecurityHeadersFilter implements ContainerResponseFilter {

View file

@ -33,6 +33,8 @@ import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.CacheControl; import jakarta.ws.rs.core.CacheControl;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;
import java.io.InputStream; import java.io.InputStream;
/** /**
@ -40,6 +42,7 @@ import java.io.InputStream;
* *
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
@Provider
@Path("/js") @Path("/js")
public class JsResource { public class JsResource {

View file

@ -71,6 +71,8 @@ import java.util.Set;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*
* Note: the classes and singletons are not used by Quarkus - see the KeycloakProcessor to do exclusions
*/ */
public class KeycloakApplication extends Application { public class KeycloakApplication extends Application {

View file

@ -22,6 +22,8 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.health.LoadBalancerCheckProvider; import org.keycloak.health.LoadBalancerCheckProvider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -37,6 +39,7 @@ import java.util.Set;
* *
* @author <a href="mailto:aschwart@redhat.com">Alexander Schwartz</a> * @author <a href="mailto:aschwart@redhat.com">Alexander Schwartz</a>
*/ */
@Provider
@Path("/lb-check") @Path("/lb-check")
@NonBlocking @NonBlocking
public class LoadBalancerResource { public class LoadBalancerResource {

View file

@ -53,6 +53,8 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.ResponseBuilder; import jakarta.ws.rs.core.Response.ResponseBuilder;
import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriBuilder;
import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.ext.Provider;
import java.net.URI; import java.net.URI;
import java.util.Comparator; import java.util.Comparator;
@ -60,6 +62,7 @@ import java.util.Comparator;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
@Provider
@Path("/realms") @Path("/realms")
public class RealmsResource { public class RealmsResource {
protected static final Logger logger = Logger.getLogger(RealmsResource.class); protected static final Logger logger = Logger.getLogger(RealmsResource.class);

View file

@ -22,7 +22,9 @@ import org.keycloak.utils.MediaType;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.ext.Provider;
@Provider
@Path("/robots.txt") @Path("/robots.txt")
public class RobotsResource { public class RobotsResource {

View file

@ -37,6 +37,7 @@ import org.keycloak.theme.Theme;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -56,6 +57,7 @@ import static java.util.stream.Collectors.toSet;
* *
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
@Provider
@Path("/resources") @Path("/resources")
public class ThemeResource { public class ThemeResource {

View file

@ -29,6 +29,8 @@ import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.ResponseBuilder; import jakarta.ws.rs.core.Response.ResponseBuilder;
import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.ext.Provider;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.common.ClientConnection; import org.keycloak.common.ClientConnection;
import org.keycloak.common.Profile; import org.keycloak.common.Profile;
@ -64,6 +66,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
@Provider
@Path("/") @Path("/")
public class WelcomeResource { public class WelcomeResource {

View file

@ -49,6 +49,8 @@ import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriBuilder;
import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.Properties; import java.util.Properties;
@ -59,6 +61,7 @@ import java.util.Properties;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
@Provider
@Path("/admin") @Path("/admin")
public class AdminRoot { public class AdminRoot {
protected static final Logger logger = Logger.getLogger(AdminRoot.class); protected static final Logger logger = Logger.getLogger(AdminRoot.class);

View file

@ -35,6 +35,7 @@ import java.util.stream.Stream;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
@Provider
public class ObjectMapperResolver implements ContextResolver<ObjectMapper> { public class ObjectMapperResolver implements ContextResolver<ObjectMapper> {
protected ObjectMapper mapper; protected ObjectMapper mapper;