Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use CertificateGenerator in OIDC Wiremock tests #44808

Merged
merged 1 commit into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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