Skip to content

Commit

Permalink
Merge pull request quarkusio#44808 from sberyozkin/fix_oidc_wiremock_…
Browse files Browse the repository at this point in the history
…tests

Use CertificateGenerator in OIDC Wiremock tests
  • Loading branch information
sberyozkin authored Dec 1, 2024
2 parents 2649fa7 + ebb2c0c commit 2d19846
Show file tree
Hide file tree
Showing 13 changed files with 120 additions and 180 deletions.
5 changes: 5 additions & 0 deletions integration-tests/oidc-wiremock/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.smallrye.certs</groupId>
<artifactId>smallrye-certificate-generator</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public Uni<OidcTenantConfig> resolve(RoutingContext context,
} else if (path.endsWith("bearer-certificate-full-chain-root-only")) {
OidcTenantConfig config = new OidcTenantConfig();
config.setTenantId("bearer-certificate-full-chain-root-only");
config.getCertificateChain().setTrustStoreFile(Path.of("truststore-rootcert.p12"));
config.getCertificateChain().setTrustStoreFile(Path.of("target/chain/truststore-rootcert.p12"));
config.getCertificateChain().setTrustStorePassword("storepassword");
config.getCertificateChain().setLeafCertificateName("www.quarkustest.com");
return Uni.createFrom().item(config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.authentication.verify-
quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.client-id=quarkus-web-app
quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.credentials.client-secret.value=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.credentials.client-secret.method=query
quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.certificate-chain.trust-store-file=truststore.p12
quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.certificate-chain.trust-store-file=target/chain/truststore.p12
quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.certificate-chain.trust-store-password=storepassword


Expand Down Expand Up @@ -172,7 +172,7 @@ quarkus.oidc.bearer-kid-or-chain.client-id=quarkus-app
quarkus.oidc.bearer-kid-or-chain.credentials.secret=secret
quarkus.oidc.bearer-kid-or-chain.token.audience=https://service.example.com
quarkus.oidc.bearer-kid-or-chain.allow-token-introspection-cache=false
quarkus.oidc.bearer-kid-or-chain.certificate-chain.trust-store-file=truststore.p12
quarkus.oidc.bearer-kid-or-chain.certificate-chain.trust-store-file=target/chain/truststore.p12
quarkus.oidc.bearer-kid-or-chain.certificate-chain.trust-store-password=storepassword

quarkus.oidc.bearer-id.auth-server-url=${keycloak.url}/realms/quarkus/
Expand All @@ -199,7 +199,7 @@ quarkus.oidc.bearer-azure.jwks-path=${keycloak.url}/azure/jwk
quarkus.oidc.bearer-azure.jwks.resolve-early=false
quarkus.oidc.bearer-azure.token.lifespan-grace=2147483647
quarkus.oidc.bearer-azure.token.customizer-name=azure-access-token-customizer
quarkus.oidc.bearer-azure.certificate-chain.trust-store-file=truststore.p12
quarkus.oidc.bearer-azure.certificate-chain.trust-store-file=target/chain/truststore.p12
quarkus.oidc.bearer-azure.certificate-chain.trust-store-password=storepassword

quarkus.oidc.bearer-role-claim-path.auth-server-url=${keycloak.url}/realms/quarkus/
Expand All @@ -215,14 +215,14 @@ quarkus.oidc.bearer-no-introspection.credentials.secret=secret
quarkus.oidc.bearer-no-introspection.token.audience=https://service.example.com
quarkus.oidc.bearer-no-introspection.token.allow-jwt-introspection=false

quarkus.oidc.bearer-certificate-full-chain.certificate-chain.trust-store-file=truststore.p12
quarkus.oidc.bearer-certificate-full-chain.certificate-chain.trust-store-file=target/chain/truststore.p12
quarkus.oidc.bearer-certificate-full-chain.certificate-chain.trust-store-password=storepassword

quarkus.oidc.bearer-chain-custom-validator.certificate-chain.trust-store-file=truststore.p12
quarkus.oidc.bearer-chain-custom-validator.certificate-chain.trust-store-file=target/chain/truststore.p12
quarkus.oidc.bearer-chain-custom-validator.certificate-chain.trust-store-password=storepassword
quarkus.oidc.bearer-chain-custom-validator.token.audience=https://service.example.com

quarkus.oidc.bearer-certificate-full-chain-root-only-wrongcname.certificate-chain.trust-store-file=truststore-rootcert.p12
quarkus.oidc.bearer-certificate-full-chain-root-only-wrongcname.certificate-chain.trust-store-file=target/chain/truststore-rootcert.p12
quarkus.oidc.bearer-certificate-full-chain-root-only-wrongcname.certificate-chain.trust-store-password=storepassword
quarkus.oidc.bearer-certificate-full-chain-root-only-wrongcname.certificate-chain.leaf-certificate-name=www.quarkusio.com

Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.awaitility.Awaitility;
import org.hamcrest.Matchers;
import org.jose4j.jwx.HeaderParameterNames;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import com.github.tomakehurst.wiremock.WireMockServer;
Expand All @@ -39,12 +38,10 @@
import io.smallrye.jwt.algorithm.SignatureAlgorithm;
import io.smallrye.jwt.build.Jwt;
import io.smallrye.jwt.build.JwtClaimsBuilder;
import io.smallrye.jwt.util.KeyUtils;
import io.smallrye.jwt.util.ResourceUtils;
import io.vertx.core.json.JsonObject;

@QuarkusTest
@QuarkusTestResource(OidcWiremockTestResource.class)
@QuarkusTestResource(CustomOidcWiremockTestResource.class)
public class BearerTokenAuthorizationTest {

@OidcWireMock
Expand All @@ -62,7 +59,6 @@ public void testSecureAccessSuccessPreferredUsername() {
}

@Test
@Disabled
public void testAccessResourceAzure() throws Exception {
String azureToken = readFile("token.txt");
String azureJwk = readFile("jwks.json");
Expand Down Expand Up @@ -192,16 +188,13 @@ public void testAccessAdminResourceWithWrongCertS256Thumbprint() {
}

@Test
@Disabled
public void testCertChainWithCustomValidator() throws Exception {
X509Certificate rootCert = KeyUtils.getCertificate(ResourceUtils.readResource("/ca.cert.pem"));
X509Certificate intermediateCert = KeyUtils.getCertificate(ResourceUtils.readResource("/intermediate.cert.pem"));
X509Certificate subjectCert = KeyUtils.getCertificate(ResourceUtils.readResource("/www.quarkustest.com.cert.pem"));
PrivateKey subjectPrivateKey = KeyUtils.readPrivateKey("/www.quarkustest.com.key.pem");
List<X509Certificate> chain = TestUtils.loadCertificateChain();
PrivateKey subjectPrivateKey = TestUtils.loadLeafCertificatePrivateKey();

// Send the token with the valid certificate chain and bind it to the token claim
String accessToken = getAccessTokenForCustomValidator(
List.of(subjectCert, intermediateCert, rootCert),
chain,
subjectPrivateKey, "https://service.example.com", true, false);

RestAssured.given().auth().oauth2(accessToken)
Expand All @@ -212,7 +205,7 @@ public void testCertChainWithCustomValidator() throws Exception {

// Send the token with the valid certificate chain but do not bind it to the token claim
accessToken = getAccessTokenForCustomValidator(
List.of(subjectCert, intermediateCert, rootCert),
chain,
subjectPrivateKey, "https://service.example.com", false, false);

RestAssured.given().auth().oauth2(accessToken)
Expand All @@ -222,7 +215,7 @@ public void testCertChainWithCustomValidator() throws Exception {

// Send the token with the valid certificate chain bound to the token claim, but expired
accessToken = getAccessTokenForCustomValidator(
List.of(subjectCert, intermediateCert, rootCert),
chain,
subjectPrivateKey, "https://service.example.com", true, true);
RestAssured.given().auth().oauth2(accessToken)
.when().get("/api/admin/bearer-chain-custom-validator")
Expand All @@ -231,7 +224,7 @@ public void testCertChainWithCustomValidator() throws Exception {

// Send the token with the valid certificate chain but with the wrong audience
accessToken = getAccessTokenForCustomValidator(
List.of(subjectCert, intermediateCert, rootCert),
chain,
subjectPrivateKey, "https://server.example.com", true, false);

RestAssured.given().auth().oauth2(accessToken)
Expand All @@ -242,16 +235,14 @@ public void testCertChainWithCustomValidator() throws Exception {
}

@Test
@Disabled
public void testAccessAdminResourceWithFullCertChain() throws Exception {
X509Certificate rootCert = KeyUtils.getCertificate(ResourceUtils.readResource("/ca.cert.pem"));
X509Certificate intermediateCert = KeyUtils.getCertificate(ResourceUtils.readResource("/intermediate.cert.pem"));
X509Certificate subjectCert = KeyUtils.getCertificate(ResourceUtils.readResource("/www.quarkustest.com.cert.pem"));
PrivateKey subjectPrivateKey = KeyUtils.readPrivateKey("/www.quarkustest.com.key.pem");
// index 2 - root, index 1 - intermediate, index 0 - leaf
List<X509Certificate> chain = TestUtils.loadCertificateChain();
PrivateKey subjectPrivateKey = TestUtils.loadLeafCertificatePrivateKey();

// Send the token with the valid certificate chain and bind it to the token claim
String accessToken = getAccessTokenWithCertChain(
List.of(subjectCert, intermediateCert, rootCert),
chain,
subjectPrivateKey);

RestAssured.given().auth().oauth2(accessToken)
Expand All @@ -268,7 +259,7 @@ public void testAccessAdminResourceWithFullCertChain() throws Exception {

// Send the token with the valid certificate chain, but with the token signed by a non-matching private key
accessToken = getAccessTokenWithCertChain(
List.of(subjectCert, intermediateCert, rootCert),
chain,
KeyPairGenerator.getInstance("RSA").generateKeyPair().getPrivate());
RestAssured.given().auth().oauth2(accessToken)
.when().get("/api/admin/bearer-certificate-full-chain")
Expand All @@ -277,7 +268,7 @@ public void testAccessAdminResourceWithFullCertChain() throws Exception {

// Send the token with the valid certificates but which are in the wrong order in the chain
accessToken = getAccessTokenWithCertChain(
List.of(intermediateCert, subjectCert, rootCert),
List.of(chain.get(1), chain.get(0), chain.get(2)),
subjectPrivateKey);
RestAssured.given().auth().oauth2(accessToken)
.when().get("/api/admin/bearer-certificate-full-chain")
Expand All @@ -286,7 +277,7 @@ public void testAccessAdminResourceWithFullCertChain() throws Exception {

// Send the token with the valid certificates but with the intermediate one omitted from the chain
accessToken = getAccessTokenWithCertChain(
List.of(subjectCert, rootCert),
List.of(chain.get(0), chain.get(2)),
subjectPrivateKey);
RestAssured.given().auth().oauth2(accessToken)
.when().get("/api/admin/bearer-certificate-full-chain")
Expand All @@ -295,7 +286,7 @@ public void testAccessAdminResourceWithFullCertChain() throws Exception {

// Send the token with the only the last valid certificate
accessToken = getAccessTokenWithCertChain(
List.of(subjectCert),
List.of(chain.get(0)),
subjectPrivateKey);
RestAssured.given().auth().oauth2(accessToken)
.when().get("/api/admin/bearer-certificate-full-chain")
Expand All @@ -305,16 +296,13 @@ public void testAccessAdminResourceWithFullCertChain() throws Exception {
}

@Test
@Disabled
public void testFullCertChainWithOnlyRootInTruststore() throws Exception {
X509Certificate rootCert = KeyUtils.getCertificate(ResourceUtils.readResource("/ca.cert.pem"));
X509Certificate intermediateCert = KeyUtils.getCertificate(ResourceUtils.readResource("/intermediate.cert.pem"));
X509Certificate subjectCert = KeyUtils.getCertificate(ResourceUtils.readResource("/www.quarkustest.com.cert.pem"));
PrivateKey subjectPrivateKey = KeyUtils.readPrivateKey("/www.quarkustest.com.key.pem");
List<X509Certificate> chain = TestUtils.loadCertificateChain();
PrivateKey subjectPrivateKey = TestUtils.loadLeafCertificatePrivateKey();

// Send the token with the valid certificate chain
String accessToken = getAccessTokenWithCertChain(
List.of(subjectCert, intermediateCert, rootCert),
chain,
subjectPrivateKey);

RestAssured.given().auth().oauth2(accessToken)
Expand All @@ -331,7 +319,7 @@ public void testFullCertChainWithOnlyRootInTruststore() throws Exception {

// Send the token with the valid certificates but which are in the wrong order in the chain
accessToken = getAccessTokenWithCertChain(
List.of(intermediateCert, subjectCert, rootCert),
List.of(chain.get(1), chain.get(0), chain.get(2)),
subjectPrivateKey);
RestAssured.given().auth().oauth2(accessToken)
.when().get("/api/admin/bearer-certificate-full-chain-root-only")
Expand All @@ -340,7 +328,7 @@ public void testFullCertChainWithOnlyRootInTruststore() throws Exception {

// Send the token with the valid certificates but with the intermediate one omitted from the chain
accessToken = getAccessTokenWithCertChain(
List.of(subjectCert, rootCert),
List.of(chain.get(0), chain.get(2)),
subjectPrivateKey);
RestAssured.given().auth().oauth2(accessToken)
.when().get("/api/admin/bearer-certificate-full-chain-root-only")
Expand All @@ -349,7 +337,7 @@ public void testFullCertChainWithOnlyRootInTruststore() throws Exception {

// Send the token with the only the last valid certificate
accessToken = getAccessTokenWithCertChain(
List.of(subjectCert),
List.of(chain.get(0)),
subjectPrivateKey);
RestAssured.given().auth().oauth2(accessToken)
.when().get("/api/admin/bearer-certificate-full-chain-root-only")
Expand All @@ -358,7 +346,6 @@ public void testFullCertChainWithOnlyRootInTruststore() throws Exception {
}

@Test
@Disabled
public void testAccessAdminResourceWithKidOrChain() throws Exception {
// token with a matching kid, not x5c
String token = Jwt.preferredUserName("admin")
Expand Down Expand Up @@ -403,14 +390,12 @@ public void testAccessAdminResourceWithKidOrChain() throws Exception {
.then()
.statusCode(401);

X509Certificate rootCert = KeyUtils.getCertificate(ResourceUtils.readResource("/ca.cert.pem"));
X509Certificate intermediateCert = KeyUtils.getCertificate(ResourceUtils.readResource("/intermediate.cert.pem"));
X509Certificate subjectCert = KeyUtils.getCertificate(ResourceUtils.readResource("/www.quarkustest.com.cert.pem"));
PrivateKey subjectPrivateKey = KeyUtils.readPrivateKey("/www.quarkustest.com.key.pem");
List<X509Certificate> chain = TestUtils.loadCertificateChain();
PrivateKey subjectPrivateKey = TestUtils.loadLeafCertificatePrivateKey();

// Send the token with the valid certificate chain
token = getAccessTokenWithCertChain(
List.of(subjectCert, intermediateCert, rootCert),
chain,
subjectPrivateKey);

TestUtils.assertX5cOnlyIsPresent(token);
Expand All @@ -429,7 +414,7 @@ public void testAccessAdminResourceWithKidOrChain() throws Exception {

// Send the token with the valid certificate chain with certificates in the wrong order
token = getAccessTokenWithCertChain(
List.of(intermediateCert, subjectCert, rootCert),
List.of(chain.get(1), chain.get(0), chain.get(2)),
subjectPrivateKey);

TestUtils.assertX5cOnlyIsPresent(token);
Expand All @@ -445,7 +430,7 @@ public void testAccessAdminResourceWithKidOrChain() throws Exception {
.groups(Set.of("admin"))
.issuer("https://server.example.com")
.audience("https://service.example.com")
.jws().keyId("1").chain(List.of(intermediateCert, subjectCert, rootCert))
.jws().keyId("1").chain(List.of(chain.get(1), chain.get(0), chain.get(2)))
.sign(subjectPrivateKey);

assertBothKidAndX5cArePresent(token, "1");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
import org.htmlunit.util.Cookie;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import com.github.tomakehurst.wiremock.WireMockServer;
Expand Down Expand Up @@ -337,7 +336,6 @@ public void testCodeFlowUserInfo() throws Exception {
}

@Test
@Disabled
public void testCodeFlowUserInfoCachedInIdToken() throws Exception {
// Internal ID token, allow in memory cache = false, cacheUserInfoInIdtoken = true
final String refreshJwtToken = generateAlreadyExpiredRefreshToken();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.quarkus.it.keycloak;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Map;

import io.quarkus.test.oidc.server.OidcWiremockTestResource;
import io.smallrye.certs.chain.CertificateChainGenerator;
import io.smallrye.jwt.util.KeyUtils;

public class CustomOidcWiremockTestResource extends OidcWiremockTestResource {
@Override
public Map<String, String> start() {
try {
generateCertificates();
} catch (Exception ex) {
throw new RuntimeException(ex);
}

return super.start();
}

private void generateCertificates() throws Exception {
File chainDir = new File("target/chain");
CertificateChainGenerator chainGenerator = new CertificateChainGenerator(chainDir)
.withCN("www.quarkustest.com");
chainGenerator.generate();

Path rootCertPath = Paths.get("target/chain/root.crt");
X509Certificate rootCert = KeyUtils.getCertificate(Files.readString(rootCertPath));

Path leafCertPath = Paths.get("target/chain/www.quarkustest.com.crt");
X509Certificate leafCert = KeyUtils.getCertificate(Files.readString(leafCertPath));

File trustStore = new File(chainDir, "truststore.p12");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
keyStore.setCertificateEntry("root", rootCert);
keyStore.setCertificateEntry("leaf", leafCert);
var fos = new FileOutputStream(trustStore);
keyStore.store(fos, "storepassword".toCharArray());
fos.close();

File trustStoreRoot = new File(chainDir, "truststore-rootcert.p12");
KeyStore keyStoreRootCert = KeyStore.getInstance("PKCS12");
keyStoreRootCert.load(null, null);
keyStoreRootCert.setCertificateEntry("root", rootCert);
var fosRootCert = new FileOutputStream(trustStoreRoot);
keyStoreRootCert.store(fosRootCert, "storepassword".toCharArray());
fosRootCert.close();

}

}
Loading

0 comments on commit 2d19846

Please sign in to comment.