diff --git a/extensions/smallrye-graphql/deployment/pom.xml b/extensions/smallrye-graphql/deployment/pom.xml index d7ab02dae4e573..71233a947fce99 100644 --- a/extensions/smallrye-graphql/deployment/pom.xml +++ b/extensions/smallrye-graphql/deployment/pom.xml @@ -70,6 +70,16 @@ quarkus-smallrye-opentracing test + + io.quarkus + quarkus-elytron-security + test + + + io.quarkus + quarkus-elytron-security-properties-file + test + diff --git a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java index 6f0295b4c26863..87ca9733ca63b9 100644 --- a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java +++ b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java @@ -191,7 +191,8 @@ void buildEndpoints( BuildProducer notFoundPageDisplayableEndpointProducer, LaunchModeBuildItem launchMode, SmallRyeGraphQLRecorder recorder, - ShutdownContextBuildItem shutdownContext) throws IOException { + ShutdownContextBuildItem shutdownContext, + BeanContainerBuildItem beanContainerBuildItem) { /* * Ugly Hack @@ -215,7 +216,7 @@ void buildEndpoints( Boolean allowGet = ConfigProvider.getConfig().getOptionalValue(ConfigKey.ALLOW_GET, boolean.class).orElse(false); - Handler executionHandler = recorder.executionHandler(allowGet); + Handler executionHandler = recorder.executionHandler(allowGet, beanContainerBuildItem.getValue()); routeProducer.produce(new RouteBuildItem(quarkusConfig.rootPath, executionHandler, HandlerType.BLOCKING)); Handler schemaHandler = recorder.schemaHandler(); diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SecurityTest.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SecurityTest.java new file mode 100644 index 00000000000000..d156ab94a277d5 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/SecurityTest.java @@ -0,0 +1,79 @@ +package io.quarkus.smallrye.graphql.deployment; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +import javax.annotation.security.RolesAllowed; +import javax.enterprise.context.ApplicationScoped; + +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Query; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.restassured.http.Header; + +public class SecurityTest extends AbstractGraphQLTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(SecuredApi.class) + .addAsResource("application-secured.properties", "application.properties") + .addAsResource("users.properties") + .addAsResource("roles.properties") + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")); + + @Test + public void testAuthenticatedUser() { + String query = getPayload("{ foo }"); + RestAssured.given() + .header(new Header("Authorization", "Basic ZGF2aWQ6cXdlcnR5MTIz")) + .body(query) + .contentType(MEDIATYPE_JSON) + .post("/graphql/") + .then() + .assertThat() + .body("errors", nullValue()) + .body("data.foo", equalTo("foo")); + } + + @Test + public void testUnauthorizedRole() { + String query = getPayload("{ bar }"); + RestAssured.given() + .header(new Header("Authorization", "Basic ZGF2aWQ6cXdlcnR5MTIz")) + .body(query) + .contentType(MEDIATYPE_JSON) + .post("/graphql") + .then() + .assertThat() + .body("errors", notNullValue()) + .body("data.bar", nullValue()); + } + + @GraphQLApi + @ApplicationScoped + public static class SecuredApi { + + @Query + @RolesAllowed("fooRole") + public String foo() { + return "foo"; + } + + @Query + @RolesAllowed("barRole") + public String bar() { + return "bar"; + } + + } + +} diff --git a/extensions/smallrye-graphql/deployment/src/test/resources/application-secured.properties b/extensions/smallrye-graphql/deployment/src/test/resources/application-secured.properties new file mode 100644 index 00000000000000..dbe0d0ba62978d --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/resources/application-secured.properties @@ -0,0 +1,5 @@ +quarkus.security.users.file.enabled=true +quarkus.security.users.file.plain-text=true +quarkus.security.users.file.users=users.properties +quarkus.security.users.file.roles=roles.properties +quarkus.http.auth.basic=true \ No newline at end of file diff --git a/extensions/smallrye-graphql/deployment/src/test/resources/roles.properties b/extensions/smallrye-graphql/deployment/src/test/resources/roles.properties new file mode 100644 index 00000000000000..ef2a67ac7e9e64 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/resources/roles.properties @@ -0,0 +1 @@ +david=fooRole \ No newline at end of file diff --git a/extensions/smallrye-graphql/deployment/src/test/resources/users.properties b/extensions/smallrye-graphql/deployment/src/test/resources/users.properties new file mode 100644 index 00000000000000..0f1cc7592d0553 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/resources/users.properties @@ -0,0 +1 @@ +david=qwerty123 \ No newline at end of file diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java index 558944a56205d9..9343d1fc4beb0f 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java @@ -13,6 +13,8 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.ManagedContext; +import io.quarkus.security.identity.CurrentIdentityAssociation; +import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.smallrye.graphql.execution.ExecutionService; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; @@ -29,9 +31,11 @@ public class SmallRyeGraphQLExecutionHandler implements Handler private static final String QUERY = "query"; private static final String OK = "OK"; private volatile ExecutionService executionService; + private final CurrentIdentityAssociation currentIdentityAssociation; - public SmallRyeGraphQLExecutionHandler(boolean allowGet) { + public SmallRyeGraphQLExecutionHandler(boolean allowGet, CurrentIdentityAssociation currentIdentityAssociation) { this.allowGet = allowGet; + this.currentIdentityAssociation = currentIdentityAssociation; } @Override @@ -50,6 +54,10 @@ public void handle(final RoutingContext ctx) { } private void doHandle(final RoutingContext ctx) { + if (currentIdentityAssociation != null) { + currentIdentityAssociation.setIdentity(QuarkusHttpUser.getSecurityIdentity(ctx, null)); + } + HttpServerRequest request = ctx.request(); HttpServerResponse response = ctx.response(); diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLRecorder.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLRecorder.java index 4924ad20bb02fb..e5c59848fe307f 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLRecorder.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLRecorder.java @@ -5,6 +5,7 @@ import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; +import io.quarkus.security.identity.CurrentIdentityAssociation; import io.quarkus.smallrye.graphql.runtime.spi.QuarkusClassloadingService; import io.quarkus.vertx.http.runtime.ThreadLocalHandler; import io.smallrye.graphql.cdi.producer.GraphQLProducer; @@ -23,8 +24,8 @@ public void createExecutionService(BeanContainer beanContainer, Schema schema) { graphQLProducer.initialize(); } - public Handler executionHandler(boolean allowGet) { - return new SmallRyeGraphQLExecutionHandler(allowGet); + public Handler executionHandler(boolean allowGet, BeanContainer beanContainer) { + return new SmallRyeGraphQLExecutionHandler(allowGet, beanContainer.instance(CurrentIdentityAssociation.class)); } public Handler schemaHandler() {