Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure that eager security handling works in native mode #19895

Merged
merged 1 commit into from
Sep 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.ws.rs.Priorities;
Expand All @@ -41,13 +41,13 @@
import org.jboss.resteasy.reactive.common.model.ResourceDynamicFeature;
import org.jboss.resteasy.reactive.common.model.ResourceFeature;
import org.jboss.resteasy.reactive.common.model.ResourceInterceptors;
import org.jboss.resteasy.reactive.common.model.ResourceMethod;
import org.jboss.resteasy.reactive.common.model.ResourceReader;
import org.jboss.resteasy.reactive.common.model.ResourceWriter;
import org.jboss.resteasy.reactive.common.processor.AdditionalReaderWriter;
import org.jboss.resteasy.reactive.common.processor.AdditionalReaders;
import org.jboss.resteasy.reactive.common.processor.AdditionalWriters;
import org.jboss.resteasy.reactive.common.processor.DefaultProducesHandler;
import org.jboss.resteasy.reactive.common.processor.EndpointIndexer;
import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames;
import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult;
import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult;
Expand Down Expand Up @@ -335,7 +335,7 @@ public void setupEndpoints(Capabilities capabilities, BeanArchiveIndexBuildItem

QuarkusServerEndpointIndexer.Builder serverEndpointIndexerBuilder = new QuarkusServerEndpointIndexer.Builder()
.addMethodScanners(
methodScanners.stream().map(MethodScannerBuildItem::getMethodScanner).collect(Collectors.toList()))
methodScanners.stream().map(MethodScannerBuildItem::getMethodScanner).collect(toList()))
.setIndex(index)
.setFactoryCreator(new QuarkusFactoryCreator(recorder, beanContainerBuildItem.getValue()))
.setEndpointInvokerFactory(new QuarkusInvokerFactory(generatedClassBuildItemBuildProducer, recorder))
Expand All @@ -353,13 +353,20 @@ public void setupEndpoints(Capabilities capabilities, BeanArchiveIndexBuildItem
.setClassLevelExceptionMappers(
classLevelExceptionMappers.isPresent() ? classLevelExceptionMappers.get().getMappers()
: Collections.emptyMap())
.setResourceMethodCallback(new Consumer<Map.Entry<MethodInfo, ResourceMethod>>() {
.setResourceMethodCallback(new Consumer<>() {
@Override
public void accept(Map.Entry<MethodInfo, ResourceMethod> entry) {
MethodInfo method = entry.getKey();
public void accept(EndpointIndexer.ResourceMethodCallbackData entry) {
MethodInfo method = entry.getMethodInfo();
String source = ResteasyReactiveProcessor.class.getSimpleName() + " > " + method.declaringClass()
+ "[" + method + "]";

ClassInfo classInfoWithSecurity = consumeStandardSecurityAnnotations(method,
entry.getActualEndpointInfo(), index, c -> c);
if (classInfoWithSecurity != null) {
reflectiveClass.produce(new ReflectiveClassBuildItem(false, true, false,
entry.getActualEndpointInfo().name().toString()));
}

reflectiveHierarchy.produce(new ReflectiveHierarchyBuildItem.Builder()
.type(method.returnType())
.index(index)
Expand Down Expand Up @@ -648,21 +655,29 @@ MethodScannerBuildItem integrateEagerSecurity(Capabilities capabilities, Combine
@Override
public List<HandlerChainCustomizer> scan(MethodInfo method, ClassInfo actualEndpointClass,
Map<String, Object> methodContext) {
if (SecurityTransformerUtils.hasStandardSecurityAnnotation(method)) {
return Collections.singletonList(new EagerSecurityHandler.Customizer());
}
ClassInfo c = actualEndpointClass;
while (c.superName() != null) {
if (SecurityTransformerUtils.hasStandardSecurityAnnotation(c)) {
return Collections.singletonList(new EagerSecurityHandler.Customizer());
}
c = index.getClassByName(c.superName());
}
return Collections.emptyList();
return Objects.requireNonNullElse(
consumeStandardSecurityAnnotations(method, actualEndpointClass, index,
(c) -> Collections.singletonList(new EagerSecurityHandler.Customizer())),
Collections.emptyList());
}
});
}

private <T> T consumeStandardSecurityAnnotations(MethodInfo methodInfo, ClassInfo classInfo, IndexView index,
Function<ClassInfo, T> function) {
if (SecurityTransformerUtils.hasStandardSecurityAnnotation(methodInfo)) {
return function.apply(methodInfo.declaringClass());
}
ClassInfo c = classInfo;
while (c.superName() != null) {
if (SecurityTransformerUtils.hasStandardSecurityAnnotation(c)) {
return function.apply(c);
}
c = index.getClassByName(c.superName());
}
return null;
}

private Optional<String> getAppPath(Optional<String> newPropertyValue) {
Optional<String> legacyProperty = ConfigProvider.getConfig().getOptionalValue("quarkus.rest.path", String.class);
if (legacyProperty.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public abstract class EndpointIndexer<T extends EndpointIndexer<T, PARAM, METHOD
private final BlockingDefault defaultBlocking;
private final Map<DotName, Map<String, String>> classLevelExceptionMappers;
private final Function<String, BeanFactory<Object>> factoryCreator;
private final Consumer<Map.Entry<MethodInfo, ResourceMethod>> resourceMethodCallback;
private final Consumer<ResourceMethodCallbackData> resourceMethodCallback;

protected EndpointIndexer(Builder<T, ?, METHOD> builder) {
this.index = builder.index;
Expand Down Expand Up @@ -549,7 +549,7 @@ private ResourceMethod createResourceMethod(ClassInfo currentClassInfo, ClassInf

handleAdditionalMethodProcessing((METHOD) method, currentClassInfo, currentMethodInfo);
if (resourceMethodCallback != null) {
resourceMethodCallback.accept(new AbstractMap.SimpleEntry<>(currentMethodInfo, method));
resourceMethodCallback.accept(new ResourceMethodCallbackData(currentMethodInfo, actualEndpointInfo, method));
}
return method;
} catch (Exception e) {
Expand Down Expand Up @@ -1144,7 +1144,7 @@ public static abstract class Builder<T extends EndpointIndexer<T, ?, METHOD>, B
private AdditionalWriters additionalWriters;
private boolean hasRuntimeConverters;
private Map<DotName, Map<String, String>> classLevelExceptionMappers;
private Consumer<Map.Entry<MethodInfo, ResourceMethod>> resourceMethodCallback;
private Consumer<ResourceMethodCallbackData> resourceMethodCallback;

public B setDefaultBlocking(BlockingDefault defaultBlocking) {
this.defaultBlocking = defaultBlocking;
Expand Down Expand Up @@ -1206,12 +1206,36 @@ public B setClassLevelExceptionMappers(Map<DotName, Map<String, String>> classLe
return (B) this;
}

public B setResourceMethodCallback(Consumer<Map.Entry<MethodInfo, ResourceMethod>> resourceMethodCallback) {
public B setResourceMethodCallback(Consumer<ResourceMethodCallbackData> resourceMethodCallback) {
this.resourceMethodCallback = resourceMethodCallback;
return (B) this;
}

public abstract T build();
}

public static class ResourceMethodCallbackData {
private final MethodInfo methodInfo;
private final ClassInfo actualEndpointInfo;
private final ResourceMethod resourceMethod;

public ResourceMethodCallbackData(MethodInfo methodInfo, ClassInfo actualEndpointInfo, ResourceMethod resourceMethod) {
this.methodInfo = methodInfo;
this.actualEndpointInfo = actualEndpointInfo;
this.resourceMethod = resourceMethod;
}

public MethodInfo getMethodInfo() {
return methodInfo;
}

public ClassInfo getActualEndpointInfo() {
return actualEndpointInfo;
}

public ResourceMethod getResourceMethod() {
return resourceMethod;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.quarkus.it.keycloak;

public class HelloResource extends HelloResourceBase implements IHelloResource {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.it.keycloak;

import javax.annotation.security.RolesAllowed;

@RolesAllowed("user")
public class HelloResourceBase {

public String hello() {
return "hello";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.quarkus.it.keycloak;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public interface IHelloResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
String hello();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.it.keycloak;

import io.quarkus.test.junit.QuarkusIntegrationTest;

@QuarkusIntegrationTest
public class HelloResourceIT extends HelloResourceTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.it.keycloak;

import static io.restassured.RestAssured.when;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class HelloResourceTest {

@Test
public void testHelloEndpoint() {
when().get("/hello")
.then()
.statusCode(401);
}
}