Skip to content

Commit

Permalink
Merge pull request quarkusio#10004 from jmartisk/master-issue-10001
Browse files Browse the repository at this point in the history
Enable programmatic security in SmallRye GraphQL endpoints
  • Loading branch information
gsmet authored Jun 18, 2020
2 parents 56eb237 + 1b5ff56 commit b139fa8
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 3 deletions.
10 changes: 10 additions & 0 deletions extensions/smallrye-graphql/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@
<artifactId>quarkus-smallrye-opentracing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-properties-file</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ void buildEndpoints(
BuildProducer<NotFoundPageDisplayableEndpointBuildItem> notFoundPageDisplayableEndpointProducer,
LaunchModeBuildItem launchMode,
SmallRyeGraphQLRecorder recorder,
ShutdownContextBuildItem shutdownContext) throws IOException {
ShutdownContextBuildItem shutdownContext,
BeanContainerBuildItem beanContainerBuildItem // don't remove this - makes sure beanContainer is initialized
) {

/*
* <em>Ugly Hack</em>
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
}

}

}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
david=fooRole
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
david=qwerty123
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,9 +31,11 @@ public class SmallRyeGraphQLExecutionHandler implements Handler<RoutingContext>
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
Expand All @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import java.util.function.Supplier;

import javax.enterprise.inject.Instance;
import javax.enterprise.inject.spi.CDI;

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;
Expand All @@ -24,7 +28,15 @@ public void createExecutionService(BeanContainer beanContainer, Schema schema) {
}

public Handler<RoutingContext> executionHandler(boolean allowGet) {
return new SmallRyeGraphQLExecutionHandler(allowGet);
Instance<CurrentIdentityAssociation> identityAssociations = CDI.current()
.select(CurrentIdentityAssociation.class);
CurrentIdentityAssociation association;
if (identityAssociations.isResolvable()) {
association = identityAssociations.get();
} else {
association = null;
}
return new SmallRyeGraphQLExecutionHandler(allowGet, association);
}

public Handler<RoutingContext> schemaHandler() {
Expand Down

0 comments on commit b139fa8

Please sign in to comment.