Skip to content

Commit

Permalink
Merge pull request #1790 from michalvavrik/feature/security-vertx-jwt
Browse files Browse the repository at this point in the history
Fix Vert.x JWT tests in FIPS-enabled environment
  • Loading branch information
gtroitsk authored May 11, 2024
2 parents 5a3b7fb + 47b1d13 commit eadf8ff
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jakarta.inject.Inject;

import io.quarkus.ts.security.vertx.config.AuthNConfig;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.JWTOptions;
import io.vertx.ext.auth.PubSecKeyOptions;
Expand All @@ -29,9 +30,9 @@ JWTAuth jwtAuth() {

private PubSecKeyOptions getPubSecKeyOptions() {
JsonObject authConfig = new JsonObject()
.put("symmetric", true)
.put("symmetric", false)
.put("algorithm", authNConf.alg())
.put("publicKey", authNConf.secret());
.put("publicKey", Buffer.buffer(CertUtils.loadKey(authNConf.certPath())));

return new PubSecKeyOptions(authConfig).setBuffer(authConfig.getBuffer("publicKey"));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.ts.security.vertx.auth;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class CertUtils {

private CertUtils() {
// UTIL CLASS
}

public static String loadKey(String path) {
try {
return Files.readString(Path.of(path));
} catch (IOException e) {
throw new RuntimeException("Failed to extract key from path %s".formatted(path), e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
public interface AuthNConfig {
String alg();

String secret();
String certPath();

@WithName("token-live-span-min")
int liveSpan();
Expand Down
3 changes: 1 addition & 2 deletions security/vertx-jwt/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

app.name=Vertx-jwt

authN.alg=HS256
authN.secret=keepSecret
authN.alg=RS256
authN.token-live-span-min=10
authN.jwt.claims.aud=third_party
authN.jwt.claims.iss=[email protected]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@

import io.quarkus.test.bootstrap.DefaultService;
import io.quarkus.test.bootstrap.RestService;
import io.quarkus.test.security.certificate.Certificate.PemCertificate;
import io.quarkus.test.security.certificate.CertificateBuilder;
import io.quarkus.test.services.Certificate;
import io.quarkus.test.services.Container;
import io.quarkus.test.services.QuarkusApplication;
import io.quarkus.ts.security.vertx.auth.CertUtils;
import io.quarkus.ts.security.vertx.model.BladeRunner;
import io.quarkus.ts.security.vertx.model.Replicant;
import io.restassured.http.ContentType;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.JWTOptions;
import io.vertx.ext.auth.PubSecKeyOptions;
import io.vertx.ext.auth.jwt.JWTAuth;
import io.vertx.ext.auth.jwt.JWTAuthOptions;
Expand All @@ -25,8 +31,9 @@
public abstract class AbstractCommonIT {

static final int REDIS_PORT = 6379;
private static final String JWT_ALGORITHM = "HS256";
private static final String JWT_SECRET = "keepSecret";
private static final String JWT_ALGORITHM = "RS256";
private static final String INVALID_PEM_PREFIX = "invalid";
private static final String VALID_PEM_PREFIX = "vertx-jwt";

BladeRunner bladeRunner;
Replicant replicant;
Expand All @@ -35,13 +42,17 @@ public abstract class AbstractCommonIT {
@Container(image = "${redis.image}", port = REDIS_PORT, expectedLog = "Ready to accept connections")
static DefaultService redis = new DefaultService().withProperty("ALLOW_EMPTY_PASSWORD", "YES");

@QuarkusApplication
@QuarkusApplication(certificates = {
@Certificate(format = Certificate.Format.PEM, prefix = VALID_PEM_PREFIX),
@Certificate(format = Certificate.Format.PEM, prefix = INVALID_PEM_PREFIX)
})
static RestService app = new RestService()
.withProperty("quarkus.redis.hosts",
() -> {
String redisHost = redis.getURI().withScheme("redis").getRestAssuredStyleUri();
return String.format("%s:%d", redisHost, redis.getURI().getPort());
});
})
.withProperty("authN.cert-path", AbstractCommonIT::getCertificatePath);

@BeforeEach
public void setup() {
Expand Down Expand Up @@ -97,6 +108,7 @@ protected String JWT(Invalidity invalidity, String... groups) {
JsonObject authConfig = defaultAuthConfig();
JsonObject claims = defaultClaims(groups);
JWTAuth jwt = JWTAuth.create(vertx.getDelegate(), new JWTAuthOptions()
.setJWTOptions(new JWTOptions().setAlgorithm(JWT_ALGORITHM))
.addPubSecKey(getPubSecKeyOptions(authConfig)));
switch (invalidity) {
case WRONG_ISSUER:
Expand All @@ -112,8 +124,11 @@ protected String JWT(Invalidity invalidity, String... groups) {
claims.put("exp", currentTimeEpoch());
break;
case WRONG_KEY:
authConfig.put("publicKey", "invalid");
// this key is invalid simply because it belongs to different keypair
var key = getPrivateKey(INVALID_PEM_PREFIX);
authConfig.put("publicKey", Buffer.buffer(key));
jwt = JWTAuth.create(vertx.getDelegate(), new JWTAuthOptions()
.setJWTOptions(new JWTOptions().setAlgorithm(JWT_ALGORITHM))
.addPubSecKey(getPubSecKeyOptions(authConfig)));
break;
}
Expand All @@ -122,10 +137,11 @@ protected String JWT(Invalidity invalidity, String... groups) {
}

private JsonObject defaultAuthConfig() {
var key = getPrivateKey(VALID_PEM_PREFIX);
return new JsonObject()
.put("symmetric", true)
.put("symmetric", false)
.put("algorithm", JWT_ALGORITHM)
.put("publicKey", JWT_SECRET);
.put("publicKey", Buffer.buffer(key));
}

private PubSecKeyOptions getPubSecKeyOptions(JsonObject authConfig) {
Expand Down Expand Up @@ -181,4 +197,28 @@ protected Replicant defaultReplicant() {

return replicant;
}

private static String getPrivateKeyPath(String prefix) {
return getPemCertificate(prefix).keyPath();
}

private static String getCertificatePath() {
return getPemCertificate(VALID_PEM_PREFIX).certPath();
}

private static String getPrivateKey(String prefix) {
return CertUtils.loadKey(getPrivateKeyPath(prefix));
}

private static PemCertificate getPemCertificate(String certPrefix) {
if (getCertBuilder().findCertificateByPrefix(certPrefix) instanceof PemCertificate pemCert) {
return pemCert;
} else {
throw new IllegalStateException("Failed to find PemCertificate");
}
}

private static CertificateBuilder getCertBuilder() {
return app.<CertificateBuilder> getPropertyFromContext(CertificateBuilder.INSTANCE_KEY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import io.quarkus.test.scenarios.QuarkusScenario;
import io.restassured.http.ContentType;

@Tag("fips-incompatible") // native-mode
@QuarkusScenario
public class BladeRunnerHandlerIT extends AbstractCommonIT {
@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package io.quarkus.ts.security.vertx;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import io.quarkus.test.scenarios.QuarkusScenario;

@Tag("fips-incompatible") // native-mode
@QuarkusScenario
public class NoSecuredResourceIT extends AbstractCommonIT {
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

import static org.hamcrest.Matchers.is;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import io.quarkus.test.scenarios.QuarkusScenario;
import io.restassured.http.ContentType;

@Tag("fips-incompatible") // native-mode
@QuarkusScenario
public class ReplicantHandlerIT extends AbstractCommonIT {
@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package io.quarkus.ts.security.vertx;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import io.quarkus.test.scenarios.QuarkusScenario;
import io.restassured.http.ContentType;

@Tag("fips-incompatible") // native-mode
@QuarkusScenario
public class SecuredResourceIT extends AbstractCommonIT {

@Test
@DisplayName("secured resource. Valid Token")
public void validJwtToken() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@
import java.nio.file.Files;
import java.nio.file.Paths;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import io.quarkus.test.scenarios.QuarkusScenario;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.impl.jose.JWK;
import io.vertx.ext.auth.impl.jose.JWT;

@Tag("fips-incompatible") // native-mode
@QuarkusScenario
public class JwtIT {
public class JwtTest {

@Test
void x5cCertificateChainTest() throws Exception {
Expand Down

0 comments on commit eadf8ff

Please sign in to comment.