Skip to content

Commit

Permalink
Add support for GraphQL WebSocket authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
rubik-cube-man committed Nov 13, 2023
1 parent 80c11e9 commit dcf2691
Show file tree
Hide file tree
Showing 8 changed files with 427 additions and 2 deletions.
60 changes: 60 additions & 0 deletions extensions/smallrye-graphql-client/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,42 @@
<artifactId>stork-service-discovery-static-list</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-deployment</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-properties-file-deployment</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client-deployment</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-keycloak-server</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-deployment</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand All @@ -83,5 +119,29 @@
</plugins>
</build>

<profiles>
<profile>
<id>test-keycloak</id>
<activation>
<property>
<name>test-containers</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>false</skip>
<systemPropertyVariables>
<keycloak.docker.image>${keycloak.docker.image}</keycloak.docker.image>
<keycloak.use.https>false</keycloak.use.https>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.quarkus.smallrye.graphql.client.deployment;

import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.*;

import java.util.concurrent.atomic.AtomicBoolean;

import jakarta.annotation.security.RolesAllowed;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Query;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager;
import io.restassured.RestAssured;
import io.smallrye.common.annotation.NonBlocking;
import io.smallrye.graphql.api.Subscription;
import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient;
import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClientBuilder;
import io.smallrye.mutiny.Multi;

/**
* This test establishes connections to the server, and ensures that if authentication has an expiry, that following the
* expiry of their access the connection is correctly terminated.
*/
@QuarkusTestResource(KeycloakTestResourceLifecycleManager.class)
public class DynamicGraphQLClientWebSocketAuthenticationExpiryTest {

static String url = "http://" + System.getProperty("quarkus.http.host", "localhost") + ":" +
System.getProperty("quarkus.http.test-port", "8081") + "/graphql";

@ConfigProperty(name = "quarkus.oidc.auth-server-url")
String keycloakRealm;

@RegisterExtension
static QuarkusUnitTest test = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(SecuredApi.class, TestResponse.class)
.addAsResource(new StringAsset("quarkus.oidc.client-id=graphql-test\n" +
"quarkus.oidc.credentials.secret=secret\n" +
"quarkus.smallrye-graphql.log-payload=queryAndVariables"),
"application.properties")
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"));

@Test
public void testAuthenticatedUserWithExpiryForSubscription() throws Exception {
String authHeader = getAuthHeader();

DynamicGraphQLClientBuilder clientBuilder = DynamicGraphQLClientBuilder.newBuilder()
.url(url)
.header("Authorization", authHeader)
.executeSingleOperationsOverWebsocket(true);

try (DynamicGraphQLClient client = clientBuilder.build()) {
AtomicBoolean ended = new AtomicBoolean(false);
AtomicBoolean receivedValue = new AtomicBoolean(false);
client.subscription("subscription { sub { value } }").subscribe().with(item -> {
assertTrue(item.hasData());
receivedValue.set(true);
}, Assertions::fail, () -> {
// Set to true to notify the test that the Auth token has expired.
ended.set(true);
});
await().untilTrue(ended);
assertTrue(receivedValue.get());
}
}

private String getAuthHeader() {
io.restassured.response.Response response = RestAssured.given()
.contentType("application/x-www-form-urlencoded")
.accept("application/json")
.formParam("username", "alice")
.formParam("password", "alice")
.param("client_id", "quarkus-service-app")
.param("client_secret", "secret")
.formParam("grant_type", "password")
.post(keycloakRealm + "/protocol/openid-connect/token");

return "Bearer " + response.getBody().jsonPath().getString("access_token");
}

public static class TestResponse {

private final String value;

public TestResponse(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}

@GraphQLApi
public static class SecuredApi {

// Seems to be a bug with SmallRye GraphQL which requires you to have a query or mutation in a GraphQLApi.
// This is a workaround for the time being.
@Query
public TestResponse unusedQuery() {
return null;
}

@Subscription
@RolesAllowed("user")
@NonBlocking
public Multi<TestResponse> sub() {
return Multi.createFrom().emitter(emitter -> emitter.emit(new TestResponse("Hello World")));
}
}
}
Loading

0 comments on commit dcf2691

Please sign in to comment.