Skip to content

Commit

Permalink
Test OpenShift certificate serving with REST client
Browse files Browse the repository at this point in the history
  • Loading branch information
michalvavrik committed Sep 6, 2024
1 parent b67a5ef commit 816c3f4
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 0 deletions.
4 changes: 4 additions & 0 deletions http/rest-client-reactive/src/test/java/hero/Hero.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package hero;

public record Hero(Long id, String name, String otherName, int level, String picture, String powers) {
}
15 changes: 15 additions & 0 deletions http/rest-client-reactive/src/test/java/hero/HeroClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package hero;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@RegisterRestClient(configKey = "hero")
public interface HeroClient {

@GET
@Path("/api/heroes/random")
Hero getRandomHero();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package hero;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RestClient;

@Path("hero-client-resource")
public class HeroClientResource {

@RestClient
HeroClient heroClient;

@GET
public Hero triggerClientToServerCommunication() {
return heroClient.getRandomHero();
}

}
17 changes: 17 additions & 0 deletions http/rest-client-reactive/src/test/java/hero/HeroResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package hero;

import java.util.random.RandomGenerator;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/api/heroes/random")
public class HeroResource {

@GET
public Hero getRandomHero() {
long random = RandomGenerator.getDefault().nextLong();
return new Hero(random, "Name-" + random, "Other-" + random, 1, "placeholder", "root");
}

}
4 changes: 4 additions & 0 deletions http/rest-client-reactive/src/test/java/hero/Villain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package hero;

public record Villain(Long id, String name, String otherName, int level, String picture, String powers) {
}
15 changes: 15 additions & 0 deletions http/rest-client-reactive/src/test/java/hero/VillainClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package hero;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@RegisterRestClient(configKey = "villain")
public interface VillainClient {

@GET
@Path("/api/villain/random")
Villain getRandomVillain();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package hero;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RestClient;

@Path("villain-client-resource")
public class VillainClientResource {

@RestClient
VillainClient villainClient;

@GET
public Villain triggerClientToServerCommunication() {
return villainClient.getRandomVillain();
}

}
17 changes: 17 additions & 0 deletions http/rest-client-reactive/src/test/java/hero/VillainResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package hero;

import java.util.random.RandomGenerator;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/api/villain/random")
public class VillainResource {

@GET
public Villain getRandomVillain() {
long random = RandomGenerator.getDefault().nextLong();
return new Villain(random, "Name-" + random, "Other-" + random, 1, "placeholder", "root");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package io.quarkus.ts.http.restclient.reactive;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import io.quarkus.test.bootstrap.Protocol;
import io.quarkus.test.bootstrap.RestService;
import io.quarkus.test.scenarios.OpenShiftScenario;
import io.quarkus.test.services.Certificate;
import io.quarkus.test.services.Certificate.ServingCertificates;
import io.quarkus.test.services.QuarkusApplication;
import io.quarkus.test.utils.AwaitilityUtils;

import hero.Hero;
import hero.HeroClient;
import hero.HeroClientResource;
import hero.HeroResource;
import hero.Villain;
import hero.VillainClient;
import hero.VillainClientResource;
import hero.VillainResource;

/**
* Test OpenShift serving certificate support and Quarkus REST client.
*/
@Tag("QUARKUS-4592")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@OpenShiftScenario
public class OpenShiftServingCertificatesIT {

private static final String HERO_CLIENT = "hero-client";
private static final String SERVER_TLS_CONFIG_NAME = "cert-serving-test-server";

@QuarkusApplication(ssl = true, certificates = @Certificate(tlsConfigName = SERVER_TLS_CONFIG_NAME, servingCertificates = {
@ServingCertificates(addServiceCertificate = true)
}), classes = { HeroResource.class, Hero.class, Villain.class,
VillainResource.class }, properties = "certificate-serving-server.properties")
static final RestService server = new RestService();

@QuarkusApplication(certificates = @Certificate(tlsConfigName = HERO_CLIENT, servingCertificates = @ServingCertificates(injectCABundle = true)), classes = {
HeroClient.class, Hero.class, HeroClientResource.class, Villain.class, VillainClient.class,
VillainClientResource.class }, properties = "certificate-serving-client.properties")
static final RestService client = new RestService()
.withProperty("quarkus.rest-client.hero.uri", () -> server.getURI(Protocol.HTTPS).getRestAssuredStyleUri());

@Order(1)
@Test
public void testSecuredCommunicationBetweenClientAndServer() {
// REST client use OpenShift internal CA
// server is configured with OpenShift serving certificates
// ad "untilAsserted": hopefully it's not necessary, but once I experienced unknown SAN,
// so to avoid flakiness I am adding here retry:
AwaitilityUtils.untilAsserted(() -> {
var hero = client.given()
.get("hero-client-resource")
.then()
.statusCode(200)
.extract()
.as(Hero.class);
assertNotNull(hero);
assertNotNull(hero.name());
assertTrue(hero.name().startsWith("Name-"));
assertNotNull(hero.otherName());
assertTrue(hero.otherName().startsWith("Other-"));
});
}

@Order(2)
@Test
public void testConfiguredTlsProtocolEnforced() {
// verifies that protocol version set in TLS config is obliged by both HTTP server and client
// REST client requires TLSv1.2
// HTTP server requires TLSv1.3
try {
client.given()
.get("villain-client-resource")
.then()
.statusCode(200)
.extract()
.as(Villain.class);
Assertions.fail(
"Expected SSL handshake failure as server requires TLS 1.3 and client requires TLS 1.2, but the failure did not occurred");
} catch (Exception e) {
assertTrue(e.getMessage().contains("Received fatal alert: protocol_version"),
"Expected SSL handshake failure as server requires TLS 1.3 and client requires TLS 1.2, but got: "
+ e.getMessage());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
quarkus.rest-client.hero.tls-configuration-name=hero-client
quarkus.rest-client.villain.tls-configuration-name=villain-client
quarkus.rest-client.villain.uri=${quarkus.rest-client.hero.uri}
quarkus.tls.villain-client.trust-store.pem.certs=${quarkus.tls.hero-client.trust-store.pem.certs}
quarkus.tls.hero-client.protocols=TLSv1.3
quarkus.tls.villain-client.protocols=TLSv1.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# the REST client use HTTPS but not mTLS
quarkus.http.ssl.client-auth=request
quarkus.http.insecure-requests=disabled
quarkus.tls.cert-serving-test-server.protocols=TLSv1.3

0 comments on commit 816c3f4

Please sign in to comment.