From d9d5ea55eaeca96e65ba94e1480c817f151a9171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Sat, 25 May 2024 14:59:12 +0200 Subject: [PATCH] Allow to configure certificate role mapping attribute --- .../runtime/CertChainPublicKeyResolver.java | 6 +- .../runtime/X509IdentityProvider.java | 54 +----- .../vertx/http/runtime/AuthRuntimeConfig.java | 34 +++- .../security/CertificateRoleAttribute.java | 183 ++++++++++++++++++ .../security/HttpSecurityRecorder.java | 6 +- .../runtime/security/HttpSecurityUtils.java | 28 +++ .../security/MtlsAuthenticationMechanism.java | 12 +- integration-tests/mtls-certificates/README.md | 112 +++++++++++ integration-tests/mtls-certificates/pom.xml | 5 + .../src/main/resources/application.properties | 10 +- ...role-mappings.txt => cn-role-mappings.txt} | 0 .../src/main/resources/ou-role-mappings.txt | 2 + .../main/resources/san-any-role-mappings.txt | 2 + .../main/resources/san-dir-role-mappings.txt | 2 + .../main/resources/san-dns-role-mappings.txt | 3 + .../main/resources/san-ip-role-mappings.txt | 3 + .../resources/san-rfc822-role-mappings.txt | 2 + .../main/resources/san-uri-role-mappings.txt | 2 + .../src/main/resources/server-keystore.jks | Bin 4369 -> 0 bytes .../src/main/resources/server-keystore.p12 | Bin 0 -> 2874 bytes .../src/main/resources/server-truststore.jks | Bin 1834 -> 0 bytes .../src/main/resources/server-truststore.p12 | Bin 0 -> 3782 bytes ...> AbstractCertificateRoleMappingTest.java} | 54 ++++-- ...AbstractSanCertificateRoleMappingTest.java | 85 ++++++++ ...T.java => CertificateRoleCnMappingIT.java} | 2 +- .../vertx/CertificateRoleCnMappingTest.java | 8 + .../vertx/CertificateRoleOuMappingTest.java | 21 ++ .../CertificateRoleSanDirMappingTest.java | 20 ++ .../CertificateRoleSanDnsMappingTest.java | 41 ++++ .../CertificateRoleSanIpMappingTest.java | 41 ++++ ...ertificateRoleSanOtherNameMappingTest.java | 20 ++ .../CertificateRoleSanRfc822MappingTest.java | 20 ++ .../CertificateRoleSanUriMappingTest.java | 20 ++ .../src/test/resources/client-keystore-1.jks | Bin 2214 -> 0 bytes .../src/test/resources/client-keystore-1.p12 | Bin 0 -> 2876 bytes .../src/test/resources/client-keystore-2.jks | Bin 2228 -> 0 bytes .../src/test/resources/client-keystore-2.p12 | Bin 0 -> 2908 bytes .../src/test/resources/client-truststore.jks | Bin 925 -> 0 bytes .../src/test/resources/client-truststore.p12 | Bin 0 -> 3782 bytes 39 files changed, 711 insertions(+), 87 deletions(-) create mode 100644 extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/CertificateRoleAttribute.java create mode 100644 integration-tests/mtls-certificates/README.md rename integration-tests/mtls-certificates/src/main/resources/{role-mappings.txt => cn-role-mappings.txt} (100%) create mode 100644 integration-tests/mtls-certificates/src/main/resources/ou-role-mappings.txt create mode 100644 integration-tests/mtls-certificates/src/main/resources/san-any-role-mappings.txt create mode 100644 integration-tests/mtls-certificates/src/main/resources/san-dir-role-mappings.txt create mode 100644 integration-tests/mtls-certificates/src/main/resources/san-dns-role-mappings.txt create mode 100644 integration-tests/mtls-certificates/src/main/resources/san-ip-role-mappings.txt create mode 100644 integration-tests/mtls-certificates/src/main/resources/san-rfc822-role-mappings.txt create mode 100644 integration-tests/mtls-certificates/src/main/resources/san-uri-role-mappings.txt delete mode 100644 integration-tests/mtls-certificates/src/main/resources/server-keystore.jks create mode 100644 integration-tests/mtls-certificates/src/main/resources/server-keystore.p12 delete mode 100644 integration-tests/mtls-certificates/src/main/resources/server-truststore.jks create mode 100644 integration-tests/mtls-certificates/src/main/resources/server-truststore.p12 rename integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/{CertificateRoleMappingTest.java => AbstractCertificateRoleMappingTest.java} (50%) create mode 100644 integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/AbstractSanCertificateRoleMappingTest.java rename integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/{CertificateRoleMappingIT.java => CertificateRoleCnMappingIT.java} (58%) create mode 100644 integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleCnMappingTest.java create mode 100644 integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleOuMappingTest.java create mode 100644 integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanDirMappingTest.java create mode 100644 integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanDnsMappingTest.java create mode 100644 integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanIpMappingTest.java create mode 100644 integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanOtherNameMappingTest.java create mode 100644 integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanRfc822MappingTest.java create mode 100644 integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanUriMappingTest.java delete mode 100644 integration-tests/mtls-certificates/src/test/resources/client-keystore-1.jks create mode 100644 integration-tests/mtls-certificates/src/test/resources/client-keystore-1.p12 delete mode 100644 integration-tests/mtls-certificates/src/test/resources/client-keystore-2.jks create mode 100644 integration-tests/mtls-certificates/src/test/resources/client-keystore-2.p12 delete mode 100644 integration-tests/mtls-certificates/src/test/resources/client-truststore.jks create mode 100644 integration-tests/mtls-certificates/src/test/resources/client-truststore.p12 diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java index d8d1999f0d20f8..54460e8cc25cdc 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java @@ -14,7 +14,7 @@ import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.TokenCertificateValidator; import io.quarkus.runtime.configuration.ConfigurationException; -import io.quarkus.security.runtime.X509IdentityProvider; +import io.quarkus.vertx.http.runtime.security.HttpSecurityUtils; import io.vertx.ext.auth.impl.CertificateHelper; public class CertChainPublicKeyResolver implements RefreshableVerificationKeyResolver { @@ -83,7 +83,7 @@ public Key resolveKey(JsonWebSignature jws, List nestingContex // Finally, check the leaf certificate if required if (!expectedLeafCertificateName.isEmpty()) { // Compare the leaf certificate common name against the configured value - String leafCertificateName = X509IdentityProvider.getCommonName(chain.get(0).getSubjectX500Principal()); + String leafCertificateName = HttpSecurityUtils.getCommonName(chain.get(0).getSubjectX500Principal()); if (!expectedLeafCertificateName.get().equals(leafCertificateName)) { LOG.errorf("Wrong leaf certificate common name: %s", leafCertificateName); throw new UnresolvableKeyException("Wrong leaf certificate common name"); @@ -106,4 +106,4 @@ public Key resolveKey(JsonWebSignature jws, List nestingContex throw new UnresolvableKeyException("Invalid certificate chain", ex); } } -} \ No newline at end of file +} diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java index 63d79961e261b0..488e195a157b3a 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java @@ -1,13 +1,8 @@ package io.quarkus.security.runtime; import java.security.cert.X509Certificate; -import java.util.Map; import java.util.Set; - -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; -import javax.security.auth.x500.X500Principal; +import java.util.function.Function; import jakarta.inject.Singleton; @@ -19,8 +14,7 @@ @Singleton public class X509IdentityProvider implements IdentityProvider { - private static final String COMMON_NAME = "CN"; - private static final String ROLES_ATTRIBUTE = "roles"; + private static final String ROLES_MAPPER_ATTRIBUTE = "roles_mapper"; @Override public Class getRequestType() { @@ -30,51 +24,15 @@ public Class getRequestType() { @Override public Uni authenticate(CertificateAuthenticationRequest request, AuthenticationRequestContext context) { X509Certificate certificate = request.getCertificate().getCertificate(); - Map> roles = request.getAttribute(ROLES_ATTRIBUTE); return Uni.createFrom().item(QuarkusSecurityIdentity.builder() .setPrincipal(certificate.getSubjectX500Principal()) .addCredential(request.getCertificate()) - .addRoles(extractRoles(certificate, roles)) + .addRoles(extractRoles(certificate, request.getAttribute(ROLES_MAPPER_ATTRIBUTE))) .build()); } - private Set extractRoles(X509Certificate certificate, Map> roles) { - if (roles == null) { - return Set.of(); - } - X500Principal principal = certificate.getSubjectX500Principal(); - if (principal == null || principal.getName() == null) { - return Set.of(); - } - Set matchedRoles = roles.get(principal.getName()); - if (matchedRoles != null) { - return matchedRoles; - } - String commonName = getCommonName(principal); - if (commonName != null) { - matchedRoles = roles.get(commonName); - if (matchedRoles != null) { - return matchedRoles; - } - } - return Set.of(); - } - - public static String getCommonName(X500Principal principal) { - try { - LdapName ldapDN = new LdapName(principal.getName()); - - // Apparently for some CN variations it might not produce correct results - // Can be tuned as necessary. - for (Rdn rdn : ldapDN.getRdns()) { - if (COMMON_NAME.equals(rdn.getType())) { - return rdn.getValue().toString(); - } - } - } catch (InvalidNameException ex) { - // Failing the augmentation process because of this exception seems unnecessary - // The common name my include some characters unexpected by the legacy LdapName API specification. - } - return null; + private static Set extractRoles(X509Certificate certificate, + Function> certificateToRoles) { + return certificateToRoles == null ? Set.of() : certificateToRoles.apply(certificate); } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/AuthRuntimeConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/AuthRuntimeConfig.java index 99d632542b63bb..ac1ebdde91f70c 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/AuthRuntimeConfig.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/AuthRuntimeConfig.java @@ -39,11 +39,41 @@ public class AuthRuntimeConfig { public Map> rolesMapping; /** - * Properties file containing the client certificate common name (CN) to role mappings. + * Client certificate attribute whose values are going to be mapped to the 'SecurityIdentity' roles + * according to the roles mapping specified in the certificate properties file. + * The attribute must be either one of the Relative Distinguished Names (RDNs) or Subject Alternative Names (SANs). + * By default, the Common Name (CN) attribute value is used for roles mapping. + * Supported values are: + *
    + *
  • 'DN_' followed by RDN type - Distinguished Name field. + * For example 'DN_CN' represents Common Name field. + * Multivalued RNDs and multiple instances of the same attributes are currently not supported. + *
  • + *
  • 'SAN_RFC822' - Subject Alternative Name field RFC 822 Name.
  • + *
  • 'SAN_DNS' - Subject Alternative Name field DNS Name.
  • + *
  • 'SAN_X400' - Subject Alternative Name field x400 Address.
  • + *
  • 'SAN_DIRECTORY' - Subject Alternative Name field Directory Name.
  • + *
  • 'SAN_EDI' - Subject Alternative Name field EDI Party Name.
  • + *
  • 'SAN_URI' - Subject Alternative Name field Uniform Resource Identifier (URI).
  • + *
  • 'SAN_IP' - Subject Alternative Name field IP Address (IP).
  • + *
  • 'SAN_OID' - Subject Alternative Name field Registered Object Identifier (OID).
  • + *
  • 'SAN_ANY' - Subject Alternative Name field Other Name. + * Please note that only simple case of UTF8 identifier mapping is support. + * For example, you can map 'other-identifier' to the SecurityIdentity roles. + * If you use 'openssl' tool, supported Other name definition would look like this: + * subjectAltName=otherName:1.2.3.4;UTF8:other-identifier + *
  • + *
+ */ + @ConfigItem(defaultValue = "DN_CN") + public String certificateRoleAttribute; + + /** + * Properties file containing the client certificate attribute value to role mappings. * Use it only if the mTLS authentication mechanism is enabled with either * `quarkus.http.ssl.client-auth=required` or `quarkus.http.ssl.client-auth=request`. *

- * Properties file is expected to have the `CN=role1,role,...,roleN` format and should be encoded using UTF-8. + * Properties file is expected to have the `CN_VALUE=role1,role,...,roleN` format and should be encoded using UTF-8. */ @ConfigItem public Optional certificateRoleProperties; diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/CertificateRoleAttribute.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/CertificateRoleAttribute.java new file mode 100644 index 00000000000000..adbae04f8bc262 --- /dev/null +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/CertificateRoleAttribute.java @@ -0,0 +1,183 @@ +package io.quarkus.vertx.http.runtime.security; + +import static io.quarkus.vertx.http.runtime.security.HttpSecurityUtils.COMMON_NAME; +import static io.quarkus.vertx.http.runtime.security.HttpSecurityUtils.getRdnValue; + +import java.nio.charset.StandardCharsets; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import javax.security.auth.x500.X500Principal; + +import org.jboss.logging.Logger; + +import io.quarkus.runtime.configuration.ConfigurationException; +import io.vertx.ext.auth.impl.asn.ASN1; + +public record CertificateRoleAttribute(Function> rolesMapper) { + + private static final Logger log = Logger.getLogger(CertificateRoleAttribute.class); + private static final String SAN_PREFIX = "SAN_"; + private static final String DN_PREFIX = "DN_"; + + CertificateRoleAttribute(String configValue, Map> roles) { + this(of(configValue.toUpperCase(), Map.copyOf(roles))); + } + + private static Function> of(String configValue, Map> roles) { + if (configValue.contains(SAN_PREFIX)) { + + return new Function>() { + @Override + public Set apply(X509Certificate certificate) { + return extractRolesFromCertSan(certificate, SAN.valueOf(configValue).generalNameType, roles); + } + }; + } else if (configValue.startsWith(DN_PREFIX)) { + + String rdnType = configValue.substring(DN_PREFIX.length()); + return new Function>() { + @Override + public Set apply(X509Certificate certificate) { + return extractRolesFromCertRdn(certificate, roles, rdnType); + } + }; + } else { + + throw new ConfigurationException("Invalid certificate role attribute '%s'".formatted(configValue), + Set.of("quarkus.http.auth.certificate-role-attribute")); + } + } + + private static Set extractRolesFromCertRdn(X509Certificate certificate, Map> roles, + String rdnType) { + X500Principal principal = certificate.getSubjectX500Principal(); + if (principal == null || principal.getName() == null) { + return Set.of(); + } + Set matchedRoles; + if (COMMON_NAME.equals(rdnType)) { + matchedRoles = roles.get(principal.getName()); + if (matchedRoles != null) { + return matchedRoles; + } + } + String rdnValue = getRdnValue(principal, rdnType); + if (rdnValue != null) { + matchedRoles = roles.get(rdnValue); + if (matchedRoles != null) { + return matchedRoles; + } + } + return Set.of(); + } + + private enum SAN { + + /** + * Subject Alternative Name field Other Name. + * Please note that only simple case of UTF8 identifier mapping is support. + * For example, you can map 'other-identifier' to the SecurityIdentity roles. + * If you use 'openssl' tool, supported Other name definition would look like this: + * subjectAltName=otherName:1.2.3.4;UTF8:other-identifier + */ + SAN_ANY(0), + /** + * Subject Alternative Name field RFC 822 Name. + */ + SAN_RFC822(1), + /** + * Subject Alternative Name field DNS Name. + */ + SAN_DNS(2), + /** + * Subject Alternative Name field x400 Address. + */ + SAN_X400(3), + /** + * Subject Alternative Name field Directory Name. + */ + SAN_DIRECTORY(4), + /** + * Subject Alternative Name field EDI Party Name. + */ + SAN_EDI(5), + /** + * Subject Alternative Name field Uniform Resource Identifier (URI). + */ + SAN_URI(6), + /** + * Subject Alternative Name field IP Address (IP). + */ + SAN_IP(7), + /** + * Subject Alternative Name field Registered Object Identifier (OID). + */ + SAN_OID(8); + + private final int generalNameType; + + SAN(int generalNameType) { + this.generalNameType = generalNameType; + } + } + + private static Set extractRolesFromCertSan(X509Certificate certificate, int generalNameType, + Map> roles) { + final Set result = new HashSet<>(); + try { + var sanList = certificate.getSubjectAlternativeNames(); + if (sanList != null && !sanList.isEmpty()) { + for (List objects : sanList) { + if (objects != null && objects.size() >= 2) { + if (objects.get(0) instanceof Integer thatGeneralNameType) { + if (thatGeneralNameType == generalNameType) { + + // special handling for Other name + if (thatGeneralNameType == 0 && objects.get(1) instanceof byte[] byteArr) { + var asn1 = ASN1.parseASN1(byteArr); + if (asn1.is(ASN1.SEQUENCE) && asn1.length() == 2) { + + var otherIdentifier = asn1.object(1); + while (otherIdentifier.length() == 1 + && otherIdentifier.is(ASN1.CONTEXT_SPECIFIC)) { + // there can be one extra context specific ASN with OpenJDK 17, hence loop + otherIdentifier = otherIdentifier.object(0); + } + + if (otherIdentifier.is(ASN1.UTF8_STRING)) { + var value = new String(otherIdentifier.binary(0), StandardCharsets.UTF_8); + if (roles.containsKey(value)) { + result.addAll(roles.get(value)); + break; + } + } + } + } + + for (int i = 1; i < objects.size(); i++) { + if (objects.get(i) instanceof String name) { + if (roles.containsKey(name)) { + result.addAll(roles.get(name)); + } + } + } + } + continue; + } + } + log.tracef("Cannot map SecurityIdentity roles from '%s' due to unsupported format", objects); + break; + } + } + } catch (CertificateParsingException e) { + log.tracef("Cannot map SecurityIdentity roles as certificate parsing failed"); + } + return Set.copyOf(result); + } +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityRecorder.java index 512542b7f670b6..e73b47d89a6e49 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityRecorder.java @@ -428,7 +428,10 @@ public void setMtlsCertificateRoleProperties(HttpConfiguration config) { roles.put((String) e.getKey(), parseRoles((String) e.getValue())); } - mtls.get().setRoleMappings(roles); + if (!roles.isEmpty()) { + var certRolesAttribute = new CertificateRoleAttribute(config.auth.certificateRoleAttribute, roles); + mtls.get().setCertificateToRolesMapper(certRolesAttribute.rolesMapper()); + } } catch (Exception e) { log.warnf("Unable to read roles mappings from %s:%s", rolesPath, e.getMessage()); } @@ -483,4 +486,5 @@ private static Set parseRoles(String value) { } return Set.copyOf(roles); } + } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityUtils.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityUtils.java index d4a4ab27e494a1..92479ac4e8725f 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityUtils.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityUtils.java @@ -2,11 +2,17 @@ import java.util.Map; +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; +import javax.security.auth.x500.X500Principal; + import io.quarkus.security.identity.request.AuthenticationRequest; import io.vertx.ext.web.RoutingContext; public final class HttpSecurityUtils { public final static String ROUTING_CONTEXT_ATTRIBUTE = "quarkus.http.routing.context"; + static final String COMMON_NAME = "CN"; private HttpSecurityUtils() { @@ -24,4 +30,26 @@ public static RoutingContext getRoutingContextAttribute(AuthenticationRequest re public static RoutingContext getRoutingContextAttribute(Map authenticationRequestAttributes) { return (RoutingContext) authenticationRequestAttributes.get(ROUTING_CONTEXT_ATTRIBUTE); } + + public static String getCommonName(X500Principal principal) { + return getRdnValue(principal, COMMON_NAME); + } + + static String getRdnValue(X500Principal principal, String rdnType) { + try { + LdapName ldapDN = new LdapName(principal.getName()); + + // Apparently for some RDN variations it might not produce correct results + // Can be tuned as necessary. + for (Rdn rdn : ldapDN.getRdns()) { + if (rdnType.equalsIgnoreCase(rdn.getType())) { + return rdn.getValue().toString(); + } + } + } catch (InvalidNameException ex) { + // Failing the augmentation process because of this exception seems unnecessary + // The RDN my include some characters unexpected by the legacy LdapName API specification. + } + return null; + } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/MtlsAuthenticationMechanism.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/MtlsAuthenticationMechanism.java index ee95746c7006c8..9fb0da9f63b0aa 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/MtlsAuthenticationMechanism.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/MtlsAuthenticationMechanism.java @@ -20,8 +20,8 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Collections; -import java.util.Map; import java.util.Set; +import java.util.function.Function; import javax.net.ssl.SSLPeerUnverifiedException; @@ -39,8 +39,8 @@ * The authentication handler responsible for mTLS client authentication */ public class MtlsAuthenticationMechanism implements HttpAuthenticationMechanism { - private static final String ROLES_ATTRIBUTE = "roles"; - Map> roles = Map.of(); + private static final String ROLES_MAPPER_ATTRIBUTE = "roles_mapper"; + private Function> certificateToRoles = null; @Override public Uni authenticate(RoutingContext context, @@ -62,7 +62,7 @@ public Uni authenticate(RoutingContext context, AuthenticationRequest authRequest = new CertificateAuthenticationRequest( new CertificateCredential(X509Certificate.class.cast(certificate))); - authRequest.setAttribute(ROLES_ATTRIBUTE, roles); + authRequest.setAttribute(ROLES_MAPPER_ATTRIBUTE, certificateToRoles); return identityProviderManager .authenticate(HttpSecurityUtils.setRoutingContextAttribute(authRequest, context)); } @@ -83,7 +83,7 @@ public Uni getCredentialTransport(RoutingContext contex return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.X509, "X509")); } - void setRoleMappings(Map> roles) { - this.roles = Collections.unmodifiableMap(roles); + void setCertificateToRolesMapper(Function> certificateToRoles) { + this.certificateToRoles = certificateToRoles; } } diff --git a/integration-tests/mtls-certificates/README.md b/integration-tests/mtls-certificates/README.md new file mode 100644 index 00000000000000..d4a8a23966e70b --- /dev/null +++ b/integration-tests/mtls-certificates/README.md @@ -0,0 +1,112 @@ +# mTLS Certificates + +## Generate certificates steps + +If prompted, trust certs and use password 'password'. + +```shell +cat < ./openssl.cnf +[req] +default_bits = 2048 +prompt = no +default_md = sha256 +distinguished_name = dn +req_extensions = req_ext + +[ dn ] +CN=client +OU=cert +O=quarkus +L=city +ST=state +C=AU + +[ req_ext ] +subjectAltName = @altNames + +[ altNames ] +otherName = 2.5.4.45;UTF8:redhat +email = certs@quarkus.io +dirName = test_dir +URI = https://www.quarkus.io/ + +[test_dir] +CN=client +OU=cert +O=quarkus +L=city +ST=state +C=AU +EOF + +cat < ./openssl-2.cnf +[req] +default_bits = 2048 +prompt = no +default_md = sha256 +distinguished_name = dn +req_extensions = req_ext + +[ dn ] +CN=localhost +OU=quarkus +O=quarkus +L=city +ST=state +C=IE + +[ req_ext ] +subjectAltName = @altNames + +[ altNames ] +otherName = 2.5.4.45;UTF8:quarkus +email = certs-1@quarkus.io +dirName = test_dir +URI = https://www.vertx.io/ + +[test_dir] +CN=localhost +OU=quarkus +O=quarkus +L=city +ST=state +C=IE +EOF + +openssl genrsa -out serverCA.key 2048 +openssl req -x509 -new -nodes -key serverCA.key \ + -sha256 -days 9000 -out serverCA.pem \ + -extensions req_ext -config openssl.cnf +openssl pkcs12 -export -name server-cert \ + -in serverCA.pem -inkey serverCA.key \ + -out server-keystore.p12 +keytool -import -alias localhost -storetype PKCS12 \ + -file serverCA.pem -keystore server-truststore.p12 -trustcacerts + +openssl genrsa -out clientCA.key 2048 +openssl req -x509 -new -nodes -key clientCA.key \ + -sha256 -days 9000 -out clientCA.pem \ + -extensions req_ext -config openssl.cnf +openssl pkcs12 -export -name client1-cert \ + -in clientCA.pem -inkey clientCA.key \ + -out client-keystore-1.p12 +keytool -import -alias client1-cert -storetype PKCS12 \ + -file clientCA.pem -keystore client-truststore.p12 -trustcacerts +keytool -import -alias client1-cert -file clientCA.pem \ + -keystore server-truststore.p12 -trustcacerts + +openssl genrsa -out client2CA.key 2048 +openssl req -x509 -new -nodes -key client2CA.key \ + -sha256 -days 9000 -out client2CA.pem \ + -extensions req_ext -config openssl-2.cnf +openssl pkcs12 -export -name client2-cert \ + -in client2CA.pem -inkey client2CA.key \ + -out client-keystore-2.p12 +keytool -import -alias client2-cert -file client2CA.pem \ + -keystore server-truststore.p12 -trustcacerts +keytool -import -alias client2-cert -file client2CA.pem \ + -keystore client-truststore.p12 -trustcacerts + +keytool -import -alias server-cert -file serverCA.pem \ + -keystore client-truststore.p12 -trustcacerts +``` \ No newline at end of file diff --git a/integration-tests/mtls-certificates/pom.xml b/integration-tests/mtls-certificates/pom.xml index 51e34e12c86ec2..fa7befcec5bdd1 100644 --- a/integration-tests/mtls-certificates/pom.xml +++ b/integration-tests/mtls-certificates/pom.xml @@ -32,6 +32,11 @@ rest-assured test + + me.escoffier.certs + certificate-generator-junit5 + test + diff --git a/integration-tests/mtls-certificates/src/main/resources/application.properties b/integration-tests/mtls-certificates/src/main/resources/application.properties index 871c0f0ee71bb0..59c1458397d74a 100644 --- a/integration-tests/mtls-certificates/src/main/resources/application.properties +++ b/integration-tests/mtls-certificates/src/main/resources/application.properties @@ -1,10 +1,8 @@ -quarkus.http.ssl.certificate.key-store-file=server-keystore.jks +quarkus.http.ssl.certificate.key-store-file=server-keystore.p12 quarkus.http.ssl.certificate.key-store-password=password -quarkus.http.ssl.certificate.key-store-key-alias=server -quarkus.http.ssl.certificate.key-store-key-password=serverpw -quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks +quarkus.http.ssl.certificate.trust-store-file=server-truststore.p12 quarkus.http.ssl.certificate.trust-store-password=password quarkus.http.ssl.client-auth=REQUIRED -quarkus.http.auth.certificate-role-properties=role-mappings.txt -quarkus.native.additional-build-args=-H:IncludeResources=.*\\.jks,-H:IncludeResources=.*\\.txt +quarkus.http.auth.certificate-role-properties=cn-role-mappings.txt +quarkus.native.additional-build-args=-H:IncludeResources=.*\\.p12,-H:IncludeResources=.*\\.txt diff --git a/integration-tests/mtls-certificates/src/main/resources/role-mappings.txt b/integration-tests/mtls-certificates/src/main/resources/cn-role-mappings.txt similarity index 100% rename from integration-tests/mtls-certificates/src/main/resources/role-mappings.txt rename to integration-tests/mtls-certificates/src/main/resources/cn-role-mappings.txt diff --git a/integration-tests/mtls-certificates/src/main/resources/ou-role-mappings.txt b/integration-tests/mtls-certificates/src/main/resources/ou-role-mappings.txt new file mode 100644 index 00000000000000..46c5261d23ac50 --- /dev/null +++ b/integration-tests/mtls-certificates/src/main/resources/ou-role-mappings.txt @@ -0,0 +1,2 @@ +cert=user,admin +quarkus=user \ No newline at end of file diff --git a/integration-tests/mtls-certificates/src/main/resources/san-any-role-mappings.txt b/integration-tests/mtls-certificates/src/main/resources/san-any-role-mappings.txt new file mode 100644 index 00000000000000..fe323b27893fff --- /dev/null +++ b/integration-tests/mtls-certificates/src/main/resources/san-any-role-mappings.txt @@ -0,0 +1,2 @@ +redhat=admin,user +quarkus=user \ No newline at end of file diff --git a/integration-tests/mtls-certificates/src/main/resources/san-dir-role-mappings.txt b/integration-tests/mtls-certificates/src/main/resources/san-dir-role-mappings.txt new file mode 100644 index 00000000000000..c192649c923dc9 --- /dev/null +++ b/integration-tests/mtls-certificates/src/main/resources/san-dir-role-mappings.txt @@ -0,0 +1,2 @@ +C\=AU\,ST\=state\,L\=city\,O\=quarkus\,OU\=cert\,CN\=client=admin,user +C\=IE\,ST\=state\,L\=city\,O\=quarkus\,OU\=quarkus\,CN\=localhost=user \ No newline at end of file diff --git a/integration-tests/mtls-certificates/src/main/resources/san-dns-role-mappings.txt b/integration-tests/mtls-certificates/src/main/resources/san-dns-role-mappings.txt new file mode 100644 index 00000000000000..5fe907c95a0ae1 --- /dev/null +++ b/integration-tests/mtls-certificates/src/main/resources/san-dns-role-mappings.txt @@ -0,0 +1,3 @@ +vertx.io=admin +redhat.com=user +quarkus.io=user \ No newline at end of file diff --git a/integration-tests/mtls-certificates/src/main/resources/san-ip-role-mappings.txt b/integration-tests/mtls-certificates/src/main/resources/san-ip-role-mappings.txt new file mode 100644 index 00000000000000..fc36d82ecdc32b --- /dev/null +++ b/integration-tests/mtls-certificates/src/main/resources/san-ip-role-mappings.txt @@ -0,0 +1,3 @@ +1.2.3.4=admin +2.3.4.5=user +3.4.5.6=user \ No newline at end of file diff --git a/integration-tests/mtls-certificates/src/main/resources/san-rfc822-role-mappings.txt b/integration-tests/mtls-certificates/src/main/resources/san-rfc822-role-mappings.txt new file mode 100644 index 00000000000000..9f089ba1b9d0b2 --- /dev/null +++ b/integration-tests/mtls-certificates/src/main/resources/san-rfc822-role-mappings.txt @@ -0,0 +1,2 @@ +certs@quarkus\.io=admin,user +certs-1@quarkus\.io=user \ No newline at end of file diff --git a/integration-tests/mtls-certificates/src/main/resources/san-uri-role-mappings.txt b/integration-tests/mtls-certificates/src/main/resources/san-uri-role-mappings.txt new file mode 100644 index 00000000000000..294d15ba462f59 --- /dev/null +++ b/integration-tests/mtls-certificates/src/main/resources/san-uri-role-mappings.txt @@ -0,0 +1,2 @@ +https\://www\.quarkus\.io/=admin,user +https\://www\.vertx\.io/=user \ No newline at end of file diff --git a/integration-tests/mtls-certificates/src/main/resources/server-keystore.jks b/integration-tests/mtls-certificates/src/main/resources/server-keystore.jks deleted file mode 100644 index c7ac8b12c43bf6b7050d904fe0795f9c0d25d142..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4369 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 viHB~s0RSXR&J7n(#(8T1wfSHMr ziHSv4lQ-0WmyJ`a&7hN3K;N#xLm^Qg{6r_*`>uW5pINtkbwY54YM$3PJVJ?PDXxliGiFr zuaS{~v4N4HnW>4XWfYKWjKrmrE1DRUkiE~y%D~*j$j@NV#K^_e#K_37p+58CiCfPn zS~9P2O*fajx9dQ@*Gm75S5KVrVYdHzEIDvD2j5J1bISOU@YIW3A%}c6$8{e2 z;I3?VdsYVOi`2E3c>@9fe~TF1A$?_o;4qh_y!_-BO$)lpLBfhXP-)RdSk<9~EM z$!~4>{O1w-qEz~4*{yX=2E44bz1tUp`2|GWiW(JU+bp5 z{^7~Q%*epFSkXYfD*5@C-1Qr|jX7}=Z~gg@0}+^pfPu)! zVBx5DIJV^*S1-TMNAXuD4_ z@56T8+25eZ-2Y|s7on%8K1*Gn_fYiIUge6d7bY1-hrh^O^nHeDX?3@?vP;~aX3n<} z+%NsU=B(0H`zL+<<-3=XhaNEbTsB|!P47sbLc|xhV1uQJ396Iq|J$$#>)IpS08xAAS4&KFU#$vi)ygvq5qb%+mj}? zzW?T6%aO-ZUB5kUIb?D38DKX!iOjhOn&Dn*dp^9s}a@YuAhb5L)N4?eIs8-+ce6CO1GTnAgGOh#ec-I)hx32xsP zXf~m?q+Qi>huwN@sh_;hLUwvHh~7LR8R;YF;;LMm9eHP(D8m-n*!(?Z7BAgZLrN15 zY@EL?;7Eo0X>8e#`TNpo4}TsIs_j`mWr^VCX<hnr~OFb>m9~A z1$L=RFBZOfVY2<@l&ZsPIo94f^IS1ja>fcb#{}ML^O=ViC*}XR{ddLU_F@K2`Rh0H zWLA6ryB8aycKQO_YT08~ysovI#jxHxEMl%AUHsvlM)%L-y(x_iF-t;aUp$>QE#vT) z*nUlsyLIdAA8avPGBZn6rAzAGgxMR!RvTn4pB-fW{`}wPocSMqnNiRXZ VY3RnOS!#SO`T5EEan5|{Pym^>tr7qL diff --git a/integration-tests/mtls-certificates/src/main/resources/server-truststore.p12 b/integration-tests/mtls-certificates/src/main/resources/server-truststore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..e04a51f68755b78d732deb8bf20691ff45ffa681 GIT binary patch literal 3782 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 literal 0 HcmV?d00001 diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleMappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/AbstractCertificateRoleMappingTest.java similarity index 50% rename from integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleMappingTest.java rename to integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/AbstractCertificateRoleMappingTest.java index 18484a0fa5f801..2150419bf4f304 100644 --- a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleMappingTest.java +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/AbstractCertificateRoleMappingTest.java @@ -10,37 +10,35 @@ import org.junit.jupiter.api.Test; import io.quarkus.test.common.http.TestHTTPResource; -import io.quarkus.test.junit.QuarkusTest; import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; -@QuarkusTest -public class CertificateRoleMappingTest { +public abstract class AbstractCertificateRoleMappingTest { @TestHTTPResource(ssl = true) URL url; @Test public void testAuthenticated() { - given().spec(getMtlsRequestSpec("client-keystore-1.jks")).get("/protected/authenticated") - .then().body(equalTo("CN=client,OU=cert,O=quarkus,L=city,ST=state,C=AU")); - given().spec(getMtlsRequestSpec("client-keystore-2.jks")).get("/protected/authenticated") - .then().body(equalTo("CN=localhost,OU=quarkus,O=quarkus,L=city,ST=state,C=IE")); + given().spec(getMtlsRequestSpec("client-keystore-1.p12")).get("/protected/authenticated") + .then().body(equalTo(getClient1Dn())); + given().spec(getMtlsRequestSpec("client-keystore-2.p12")).get("/protected/authenticated") + .then().body(equalTo(getClient2Dn())); } @Test public void testAuthorizedUser() { - given().spec(getMtlsRequestSpec("client-keystore-1.jks")).get("/protected/authorized-user") - .then().body(equalTo("CN=client,OU=cert,O=quarkus,L=city,ST=state,C=AU")); - given().spec(getMtlsRequestSpec("client-keystore-2.jks")).get("/protected/authorized-user") - .then().body(equalTo("CN=localhost,OU=quarkus,O=quarkus,L=city,ST=state,C=IE")); + given().spec(getMtlsRequestSpec("client-keystore-1.p12")).get("/protected/authorized-user") + .then().body(equalTo(getClient1Dn())); + given().spec(getMtlsRequestSpec("client-keystore-2.p12")).get("/protected/authorized-user") + .then().body(equalTo(getClient2Dn())); } @Test public void testAuthorizedAdmin() { - given().spec(getMtlsRequestSpec("client-keystore-1.jks")).get("/protected/authorized-admin") - .then().body(equalTo("CN=client,OU=cert,O=quarkus,L=city,ST=state,C=AU")); - given().spec(getMtlsRequestSpec("client-keystore-2.jks")).get("/protected/authorized-admin") + given().spec(getMtlsRequestSpec("client-keystore-1.p12")).get("/protected/authorized-admin") + .then().body(equalTo(getClient1Dn())); + given().spec(getMtlsRequestSpec("client-keystore-2.p12")).get("/protected/authorized-admin") .then().statusCode(403); } @@ -57,12 +55,28 @@ public void testNoClientCertificate() { "Insecure requests must fail at the transport level"); } - private RequestSpecification getMtlsRequestSpec(String clientKeyStore) { - return new RequestSpecBuilder() + protected RequestSpecification getMtlsRequestSpec(String clientKeyStore) { + var builder = new RequestSpecBuilder() .setBaseUri(String.format("%s://%s", url.getProtocol(), url.getHost())) - .setPort(url.getPort()) - .setKeyStore(clientKeyStore, "password") - .setTrustStore("client-truststore.jks", "password") - .build(); + .setPort(url.getPort()); + withKeyStore(builder, clientKeyStore); + withTrustStore(builder); + return builder.build(); + } + + protected void withKeyStore(RequestSpecBuilder requestSpecBuilder, String clientKeyStore) { + requestSpecBuilder.setKeyStore(clientKeyStore, "password"); + } + + protected void withTrustStore(RequestSpecBuilder requestSpecBuilder) { + requestSpecBuilder.setTrustStore("client-truststore.p12", "password"); + } + + protected String getClient1Dn() { + return "C=AU,ST=state,L=city,O=quarkus,OU=cert,CN=client"; + } + + protected String getClient2Dn() { + return "C=IE,ST=state,L=city,O=quarkus,OU=quarkus,CN=localhost"; } } diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/AbstractSanCertificateRoleMappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/AbstractSanCertificateRoleMappingTest.java new file mode 100644 index 00000000000000..1d24ef26fe5df8 --- /dev/null +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/AbstractSanCertificateRoleMappingTest.java @@ -0,0 +1,85 @@ +package io.quarkus.it.vertx; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + +import io.restassured.builder.RequestSpecBuilder; +import io.restassured.config.RestAssuredConfig; +import io.restassured.config.SSLConfig; + +public abstract class AbstractSanCertificateRoleMappingTest extends AbstractCertificateRoleMappingTest { + + private File client1Keystore = null; + private File client2Keystore = null; + + @Override + protected void withKeyStore(RequestSpecBuilder requestSpecBuilder, String clientKeyStore) { + // requestSpecBuilder.setKeyStore(Keystore) doesn't work, so let's create temp file + final File clientKeyStoreFile; + if (clientKeyStore.startsWith("client-keystore-1")) { + // client 1 + if (client1Keystore == null) { + client1Keystore = createClientKeystore("client1", "client2"); + } + clientKeyStoreFile = client1Keystore; + } else { + // client 2 + if (client2Keystore == null) { + client2Keystore = createClientKeystore("client2", "san"); + } + clientKeyStoreFile = client2Keystore; + } + requestSpecBuilder + .setConfig(RestAssuredConfig + .config() + .sslConfig(SSLConfig + .sslConfig() + .keyStore(clientKeyStoreFile, "password"))); + } + + protected abstract String getClientTruststorePath(); + + protected abstract String getClientKeystorePath(); + + @Override + protected void withTrustStore(RequestSpecBuilder requestSpecBuilder) { + requestSpecBuilder.setTrustStore(getClientTruststorePath(), "password"); + } + + private File createClientKeystore(String clientName, String aliasToRemove) { + try { + KeyStore ks = KeyStore.getInstance("PKCS12"); + File tsFile = new File(getClientKeystorePath()); + try (var is = new FileInputStream(tsFile)) { + ks.load(is, "password".toCharArray()); + + // create client keystore without "other" alias as you can't select alias with RestAssured + ks.deleteEntry(aliasToRemove); + File newClientKeystore = Files.createTempFile(clientName, "keystore").toFile(); + try (FileOutputStream fos = new FileOutputStream(newClientKeystore)) { + ks.store(fos, "password".toCharArray()); + } + return newClientKeystore; + } + } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) { + throw new RuntimeException("Failed to load keystore", e); + } + } + + @Override + protected String getClient1Dn() { + return "CN=client-1"; + } + + @Override + protected String getClient2Dn() { + return "CN=client-2"; + } +} diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleMappingIT.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleCnMappingIT.java similarity index 58% rename from integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleMappingIT.java rename to integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleCnMappingIT.java index 2e33b3dc59c98e..77663f7502b3a4 100644 --- a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleMappingIT.java +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleCnMappingIT.java @@ -3,5 +3,5 @@ import io.quarkus.test.junit.QuarkusIntegrationTest; @QuarkusIntegrationTest -public class CertificateRoleMappingIT extends CertificateRoleMappingTest { +public class CertificateRoleCnMappingIT extends CertificateRoleCnMappingTest { } diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleCnMappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleCnMappingTest.java new file mode 100644 index 00000000000000..26591e31f480be --- /dev/null +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleCnMappingTest.java @@ -0,0 +1,8 @@ +package io.quarkus.it.vertx; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class CertificateRoleCnMappingTest extends AbstractCertificateRoleMappingTest { + +} diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleOuMappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleOuMappingTest.java new file mode 100644 index 00000000000000..71dd0f11403fd0 --- /dev/null +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleOuMappingTest.java @@ -0,0 +1,21 @@ +package io.quarkus.it.vertx; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +@TestProfile(CertificateRoleOuMappingTest.CertDnOuTestProfile.class) +@QuarkusTest +public class CertificateRoleOuMappingTest extends AbstractCertificateRoleMappingTest { + + public static class CertDnOuTestProfile implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + return Map.of("quarkus.http.auth.certificate-role-properties", "ou-role-mappings.txt", + "quarkus.http.auth.certificate-role-attribute", "DN_OU"); + } + } + +} diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanDirMappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanDirMappingTest.java new file mode 100644 index 00000000000000..264b62dd3dc873 --- /dev/null +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanDirMappingTest.java @@ -0,0 +1,20 @@ +package io.quarkus.it.vertx; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +@TestProfile(CertificateRoleSanDirMappingTest.CertSanTestProfile.class) +@QuarkusTest +public class CertificateRoleSanDirMappingTest extends AbstractCertificateRoleMappingTest { + + public static class CertSanTestProfile implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + return Map.of("quarkus.http.auth.certificate-role-properties", "san-dir-role-mappings.txt", + "quarkus.http.auth.certificate-role-attribute", "SAN_DIRECTORY"); + } + } +} diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanDnsMappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanDnsMappingTest.java new file mode 100644 index 00000000000000..573f17d9f2edaf --- /dev/null +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanDnsMappingTest.java @@ -0,0 +1,41 @@ +package io.quarkus.it.vertx; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Alias; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; + +@Certificates(baseDir = "target/certs/san-dns", certificates = { + @Certificate(name = "san", password = "password", client = true, formats = Format.PKCS12, aliases = { + @Alias(name = "client2", cn = "client-2", password = "password", subjectAlternativeNames = "DNS:quarkus.io", client = true) + }, cn = "client-1", subjectAlternativeNames = { "DNS:redhat.com", "DNS:vertx.io" }) +}) +@TestProfile(CertificateRoleSanDnsMappingTest.CertSanTestProfile.class) +@QuarkusTest +public class CertificateRoleSanDnsMappingTest extends AbstractSanCertificateRoleMappingTest { + + @Override + protected String getClientTruststorePath() { + return "target/certs/san-dns/san-client-truststore.p12"; + } + + @Override + protected String getClientKeystorePath() { + return "target/certs/san-dns/san-client-keystore.p12"; + } + + public static class CertSanTestProfile implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + return Map.of("quarkus.http.auth.certificate-role-properties", "san-dns-role-mappings.txt", + "quarkus.http.auth.certificate-role-attribute", "SAN_DNS", + "quarkus.http.ssl.certificate.key-store-file", "target/certs/san-dns/san-keystore.p12", + "quarkus.http.ssl.certificate.trust-store-file", "target/certs/san-dns/san-server-truststore.p12"); + } + } +} diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanIpMappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanIpMappingTest.java new file mode 100644 index 00000000000000..d4f49609139ac6 --- /dev/null +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanIpMappingTest.java @@ -0,0 +1,41 @@ +package io.quarkus.it.vertx; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Alias; +import me.escoffier.certs.junit5.Certificate; +import me.escoffier.certs.junit5.Certificates; + +@Certificates(baseDir = "target/certs/san-ip", certificates = { + @Certificate(name = "san", password = "password", client = true, formats = Format.PKCS12, aliases = { + @Alias(name = "client2", cn = "client-2", password = "password", subjectAlternativeNames = "IP:3.4.5.6", client = true) + }, cn = "client-1", subjectAlternativeNames = { "IP:1.2.3.4", "IP:2.3.4.5" }) +}) +@TestProfile(CertificateRoleSanIpMappingTest.CertSanTestProfile.class) +@QuarkusTest +public class CertificateRoleSanIpMappingTest extends AbstractSanCertificateRoleMappingTest { + + @Override + protected String getClientTruststorePath() { + return "target/certs/san-ip/san-client-truststore.p12"; + } + + @Override + protected String getClientKeystorePath() { + return "target/certs/san-ip/san-client-keystore.p12"; + } + + public static class CertSanTestProfile implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + return Map.of("quarkus.http.auth.certificate-role-properties", "san-ip-role-mappings.txt", + "quarkus.http.auth.certificate-role-attribute", "SAN_IP", + "quarkus.http.ssl.certificate.key-store-file", "target/certs/san-ip/san-keystore.p12", + "quarkus.http.ssl.certificate.trust-store-file", "target/certs/san-ip/san-server-truststore.p12"); + } + } +} diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanOtherNameMappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanOtherNameMappingTest.java new file mode 100644 index 00000000000000..56348ef3745355 --- /dev/null +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanOtherNameMappingTest.java @@ -0,0 +1,20 @@ +package io.quarkus.it.vertx; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +@TestProfile(CertificateRoleSanOtherNameMappingTest.CertSanTestProfile.class) +@QuarkusTest +public class CertificateRoleSanOtherNameMappingTest extends AbstractCertificateRoleMappingTest { + + public static class CertSanTestProfile implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + return Map.of("quarkus.http.auth.certificate-role-properties", "san-any-role-mappings.txt", + "quarkus.http.auth.certificate-role-attribute", "SAN_ANY"); + } + } +} diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanRfc822MappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanRfc822MappingTest.java new file mode 100644 index 00000000000000..59e794d41cbca3 --- /dev/null +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanRfc822MappingTest.java @@ -0,0 +1,20 @@ +package io.quarkus.it.vertx; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +@TestProfile(CertificateRoleSanRfc822MappingTest.CertSanTestProfile.class) +@QuarkusTest +public class CertificateRoleSanRfc822MappingTest extends AbstractCertificateRoleMappingTest { + + public static class CertSanTestProfile implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + return Map.of("quarkus.http.auth.certificate-role-properties", "san-rfc822-role-mappings.txt", + "quarkus.http.auth.certificate-role-attribute", "SAN_RFC822"); + } + } +} diff --git a/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanUriMappingTest.java b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanUriMappingTest.java new file mode 100644 index 00000000000000..4ab2146cec249d --- /dev/null +++ b/integration-tests/mtls-certificates/src/test/java/io/quarkus/it/vertx/CertificateRoleSanUriMappingTest.java @@ -0,0 +1,20 @@ +package io.quarkus.it.vertx; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +@TestProfile(CertificateRoleSanUriMappingTest.CertSanTestProfile.class) +@QuarkusTest +public class CertificateRoleSanUriMappingTest extends AbstractCertificateRoleMappingTest { + + public static class CertSanTestProfile implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + return Map.of("quarkus.http.auth.certificate-role-properties", "san-uri-role-mappings.txt", + "quarkus.http.auth.certificate-role-attribute", "SAN_URI"); + } + } +} diff --git a/integration-tests/mtls-certificates/src/test/resources/client-keystore-1.jks b/integration-tests/mtls-certificates/src/test/resources/client-keystore-1.jks deleted file mode 100644 index cf6d6ba454864d18322799afac37f520673193d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2214 zcmcJQ`8O2&9>-_54#_?lreR2^8EGsf5m^giEKx+VWCk-tSu%>TFQG7!wawCFxDt1) z$&$558ao-vR`!gZd7gXkInO_Ee|Z1!I_G`9pY!>AzUTefU)o;+001DafPV|-e$)Fp zk-|kHUfYHU06+m)Dr65U1mjnM0U^MnAQ2!C3V=`{Y~pr7{iGM2;kEYzjSPZ7q9r%T zzQd$tc?+tj?ncxmVd`4L6BK!)Z-fwDm^`G}mK(Y2EM+!Pj`lgrsrJg@^?ZUUMf+9S zb$3jkBB_2WaIFmZdHlc<=hRDRlY5pevhC%>O-`2)l-y__$OB;n#8 zcs3**>MX{B3)};)HziYcmcvIR-dyS*V9sJum6L+Wy4gF3)lb{X?fxo-w|k2-JV*5M zW_@%NWtS@}s+4%_R$w)X$TxE4DA}-?{J?t`)drk|JW1vyxJdkF_Z}^{sLN8$dazje zb(9ug=(48*>>So`T-N)DLF-T}=5uE!{m69F0;RA-@rBpVq*(Et7lm8%ax@^KmH~=F)MVeABtWz zj_2v0zkl=<+;jI9t60&nFG$dAf-Ut$nvIWw8A}9kWGufYqegF4THNT(?%V#CefCz) zl*CL~jAo*5ZQSx)bd1gx`Yk2myuym#g%(>#WXmec0hJfOd6l}d=4!W-F7;Kc78_qJ zrYg-OUD={O*Gm?PYER)M*jqeb48_|4eFtq&-JimlkVRLn^8(%Ph zi3_(Cddo6Ut(!|VVJIc+wF@ZJiXVQY>@AF}Z@yTpB>(Z+cIfvja+>{VW8){3qHLW~L@8P<5~XEsR8n5V_&H46$yc*D zh=%X6Zj}i?ND&KumSL87kY8z{CNp~|b+92uI6vvG;CP15tQF3j5FD2x)`daP1L7aN zM*A#I>}4Cst8~th7b1j++S{u3>o=h@^ICcik!t~G#4S?rjZ24M9@}v<5>|a3nX)v* zNoq4I^xgCL$Ia)5nU@M1p$^lCrEdXc(dKJHCr1};b6H#I{O@HBsKsD;tz&Ezfnf^l zv5A+0ack_=h{X7zEXz00L;fVal)fUn5S5ZnRYr$XX=;aZc?K1KLAO`T@v?NDXBo6B zE!|IVW$vJCs_YM2BPrsnsg=904M(5IJD?dv-!nq+T1VAcVx#fp@1gW>#tSsudEWs_ z0#W;_)G2eu6Ly{f>07n*g9DDv&4bg_f)|dOF3FMbn5qEmD-GJ^!5wuW9wtd+ zD1)OT!~7tvl0hicD$@!jNQB{b(*+T!TgO5H?&%lz=K-e&cV6j*G}ufPSl65OSe>oU z2n7eGXS)@ie73kdD675O*7@efl>pAYH#;?9q+fGdFM7_{lJ6AtoBZ)uHE>A zpNaDEOFXD%Fiu!4U9uPBsE zaid(v!Lb5=F^?$3-J24MJHQQBF7k`=1O&MS`Ua8zXAs~Tt_MOc5@uTKS{|JgG49)PJpVeX*-@`I>AAb^fMS1ikLboS|1Gqsyf*dK zHG0NqbLL))x|HKP846)$q;k7%6Yh|?tK%WUWBL)4Z|~yi)2m}*HCcgKwCv@WoUo&$ zbR@7~v^HdROTJ2G)-m@GvvJokxO4`(f4f>E=6ZxA`D7TV|b!^>P3<~j#oFoJ!QMEGL70`0V7a6h(UJ2 zB3q+&A>(#yIdA#s`}xBz(vO~<*LQ{A(adfwx<=am!g*E{O9m**efmt4d1<;o?&7T2 z-4A<-Z2xFB9E*2`j<qH%rIl@hEjwumSpS+*(Q;ti6l!i7-Y#3l^DAy34^R5OR{8Y82gg4 z%ktO;N%inxEY%R%X6W^t_dWIT{cxY_{D1%Jocr7FT-q9Fo@hzA4&91e~@4G?YnJ3zS57twNmA_9FJ%<)SJ z`E)zksu2X@0H6_2j{p7!fa(Rm>}^TVUZq|wM| za6!q}yO*iY&jhA)MUpBtA=T+T_`b2P%h~PbIS8)wO1{uMF}tSvHZHL-^DVwuxBgrr z29sO&+R_qukq5Ewxz)6kH?XqJdgjUmtXWH^^xPd?_PHQceZH zjE0Ltp;nrh%(yV}^(P=k0*<#}9VIYG#w|h=Dq7GlnYgmFO+nhKLu|WqkWNcQdy$iD zh8+4a^84$V86;O)&Q4*?a0`&}te{etre8L$@JaoGQLs1kLRO*tEt&Vh^bj{C>VwrSYa_NHudsdr)b&9i>?k7T!gX(AGAIaQA9 zd~UwTFIrEQ({SH=v)fxa`3Yy(vxs!$guC39Bxm#?=Z6(})|Hs0`T}|#YpYxWE!oJ< z7t7tUrLBRX`rh+MdErx3{5co2G)2@U4fXYFvOn;p%3}#r@2uB-Rg=%k0woYL@xt{O@$DGhFzvvceYX6YzW`V z=X3sME%(o}(aVo4Nt-axD76l=xG`(LE^V_O{`1KwA!8NwJ05STP#}SJWbT{VG3&(4 z`cadmJKSqZuR8{*B)%3{iOd_p3oorm)Mq)I@xod^_RxL(ie`X*Oes@A30Qa z_B(fR7!K!R^3Gl>a!^)Hx43zbpP{P7+03HI<&55`8yE+H)%YU~h@;?#otSySi^UGX z3%%d>TfOW^ZyMX#KgK;^U}Qfvp&tlVdrVH zXJyuItamdBIx3X-ZxOmx9TO!qsnpZGPh)5A5lY&!9Oek&x+^|{+oprb>K8}C@tazg z1S0|{ub^*J#_o*zFn$;WkC2d4S}Sm zS)#V#X-7Hvy2G|21tUyBxm>QFokP?*0*zwaw#tugKAev_0zsyXgDf_b zFZCpL31%zQnX3$*Skj!4vl#N(J|<+XYH<`=*wkveT8(o^zXFBigQFoi zhdAT#r9wIX=T-z92s$j45251!6XyMOEni|4ci#UM=4)6#Ux5^)2H`@-v;MM*0!M>~ zZ)EXm+n{!E)sn}5dCj4|r20*Cx)iX~6g zN6Lf;=wdR*L>7tR{&}GowBd|RdU>tk&zJk`k!zlMvUj>J#W&vDT3+VNN&Ly1tC{=t zvpv+u%!uhRjivW?TG9I{uA@Wfq_K2)d4W1ftl#y0qzK4_TKA6+;fljoWlcs|iiquk zy3NMphWddLq)2{;ObU`Sxzog)Lh8TVHuDita`(0xDJV&CX+wON`-(iM=l+?fbNOr; zjmJX4E1wmlXqI+L`r097M+Gz4xH@9y`0ZLQf1&ZX`POyb-^g<=r8n?NtV@@MbeX;N zPhHGf$^b^F>TDuy8f|ycuF@d04FJc!0WdRd}3DD81Lp zcVC91iZ1h4D)yi5lO3|VU1FzJ6D$Wmo}H&}Nxvk{e$zz0nv>Ny5A#~#tt1IQ*p-kM zXe+WyS-U4ZCYGrFwpL0&FJULhrW~QjQ`V=h+7ESyIi2;WiylP1@@{7!TrnlyniO7H z#>CDkW6uljDt!Hj;t;<+!bGz0Vi2fFRCS2Y+Ih1!a{s{!&iw{0)y&GGrCQJ45BPa_ zvRp??$l#%^;xFSN2L3v?t}wHmwCS{;Nno#B_TASCNig$s%$|LrMNDVwlVf2vN)qWQ zBh^e;L+J|`94PPl6gBmvUCC{(Nrd(6AJQ9Im+n5h;1ZE(D4lx^I*O>>92w#lIXY5C^OH3d@C@8&_v%C6$dJvO?cDPpzCAgx^{nilPM|ZjIxq4Jja;UBau(jHJ&S z(;y*`EA_Suso9&jdjO*f!%&R_9Jvb|ZT;iD3i8y3^E=0`-7LJYCKtOw&Yrjx9xnxw z8Ma&E1o^fooMXO!A{MAxCryI!HA#|m30mfc-Q51LcSlLrVpr@u2{E&i~^|57v4ZjKLw2puEDY6m?O;h)Ut`GPUJHY6t?v+XXU zD`D$Xo@`u|PZ;>NFs8zn5T>b=_5+t&=tcc|f(PShaQTQ{6vfz*+^+psbZmF62x)N$ z{k~2!^ZdsqQ6JGQPeKZ%5gF+z$L^u|a7V}>=-^r^UD*y)rh-QOSrSwh5jz4P0B!<20d4?az-@psK=ClU0RjNG(FSN`G}rID z69j|+z~Uo;7qeUno<6I_)i~viYn=XsfzzFAbT*ddWZ%H`WC37~IFrS|nxe?!f1Ugv DoCPyQ literal 0 HcmV?d00001 diff --git a/integration-tests/mtls-certificates/src/test/resources/client-keystore-2.jks b/integration-tests/mtls-certificates/src/test/resources/client-keystore-2.jks deleted file mode 100644 index 6961a6b1d02030386c861968cf3ac3ae87699190..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2228 zcmchY`9IW+7RTp1n~)L0WUQg5;+gqcCNzyvWGPD`CZxu`Z!^SWZDb-_wkLa1L`Zfa zyCIRSnAybP4&1rHVYAX`962M( z0GL)pF18%_IA;?w$vnEnNI)(B7D?doCrQ^n$;(iZPzHIe;r5nRY!FBCP?F2!%<7A@ zsw`P{V|hOZLI3`S^gEgp71g|3$9f`CWT%2VD#LRLs6Wv{tQS27#8n;QiYgx-aOe(J zu!6Hq-Y?}|;3~=#t!V7q1U4Jwh3fh`f#{O%K$l$~F;qB7VR0zLLZsjh!E;a4JfH8Y z#pb;KRyjK=rg?^cMR7#+bYat`PRjMrj%=E4M^7#l36%z|tqG^n_LWQ2W>EdwlV6stY1^(n&D; zJ}39A9^wG~k4VGA9TXcnLEWjSEr8{c*sIsmgiEW$S%OR2 z3QfZM{WI_`0H(o9Qzpiy<{OcTH_ z!LyH_OZUXQ>Z@yb=zHcQwJ)^^d;k6NXfR0JKejNxj=l+vaQ&FNc=Jkq^`r)~y=Lb` z!CjZoJxu6~*;1>9*4;j?^zLHum=C)uI$2D?j$(;CE{h%wT{{`S-LhNvCv$q!DK;rv9*f>4bDIJw1)u&$?Xy^x(Y zUBUd~%+9IzMZ2Sffp3GMYy81g@t5kbb{K_v(T<_OYZ zwjdpyZO=NvpFZbEOZO*Kl?_m zHOZN(_3CD?|7owO?08TqL$8P^?DVYb(moE!dED9p?0$mt>fD>2TN#bWqlunWxSz4b zjq4%vDAb!xKh11Dr+U-lQTULRh%O^OQF z>)w`VG*!Q+nE!6{cFfilRD>AZjkhwHq8+f}2AV3bnynaB4c7&U!J^ko*12ce*97|0 zCwSZ6i+h)K$I6%*e|OH@O2m8mh7-iq%xi*Km&ojMx*JZf|2%Vm>7QGkJJmQ2)$UWs z-o!RXiIUgV4Zy5j(>eLQ^A;&o7*Re}k*0W_TGu{0ZGs&;EhF!hiP3FI*orNo*vpA? z&g9k4L%4wG!e?4k5+;(5wccLO#tgxscS)xL!LmEOA95tQ+Hq)$ diff --git a/integration-tests/mtls-certificates/src/test/resources/client-keystore-2.p12 b/integration-tests/mtls-certificates/src/test/resources/client-keystore-2.p12 new file mode 100644 index 0000000000000000000000000000000000000000..cd310ca6c3611d5da38c817c516deea538fe6baa GIT binary patch literal 2908 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 fiI`{T#xcI2cI8JL?G`56qF7`d357#SJb>>3m5 zZ=H#Lv+59Q`XPs%cW*|zcHdloqAxePpx)#s-v#GfEiaLcc0X6@y`H-%=v#*1=Oe7K z)qW-B>_$?)l+O%qOP zY5rU~k%^g+fpM{-fxLk%FydtSSj1RFgzrhQ6sa|LY5ME@@w;|V$wlpR7jhs1(+w~X z85yJu_Xh_F$};l(IqMm5)_GpSy6dL=5m%H>zWY>H{I`CVzt}sMyehA$jr&-7PpP*z zM48ljF&z(>+8^ZC_}KfmbhqPb-)8xgH)l67pO-z~qM~p}$NWZ#hk(j5-lwf^Z+9IR zc2RuKX)N84ko-LLoWpNbiHME=d^>D69Nu<9Q%!uM)C=*$E z`%UAe|2n@mpUG}^eHO=ZUMDN|=Mhak)*TB!DV*2nlr){SFL#&7L?v}&&wkHmlGl&k z3^#C^8TY=sB=b?#=~rQg60Xlac=V9r%)_@Gyd?kf1a~&vU$Fg3$o1A5_JX^7TZ`vz kyRg-4({opScL4{{nL7JKu02z^Frzkj_mi(*L+#BK0Mrvya{vGU diff --git a/integration-tests/mtls-certificates/src/test/resources/client-truststore.p12 b/integration-tests/mtls-certificates/src/test/resources/client-truststore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..d7001954e4a4c5a9093512315272922e93ef30de GIT binary patch literal 3782 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