TOMEE-2247 - Added support for MP Configuration.
Project: http://git-wip-us.apache.org/repos/asf/tomee/repo Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/458da041 Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/458da041 Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/458da041 Branch: refs/heads/master Commit: 458da0419c8225a7a4b6af9da03921b3ef388b9d Parents: 3b717d9 Author: Roberto Cortez <[email protected]> Authored: Wed Sep 12 12:30:48 2018 +0100 Committer: Roberto Cortez <[email protected]> Committed: Fri Dec 7 18:08:35 2018 +0000 ---------------------------------------------------------------------- .../tomee/microprofile/jwt/MPJWTFilter.java | 22 ++++-- .../microprofile/jwt/cdi/MPJWTCDIExtension.java | 2 + .../config/ConfigurableJWTAuthContextInfo.java | 78 ++++++++++++++++++++ .../jwt/AppDeploymentExtension.java | 18 ++++- 4 files changed, 113 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tomee/blob/458da041/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java index 78f059e..ee3be1b 100644 --- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java +++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java @@ -16,6 +16,7 @@ */ package org.apache.tomee.microprofile.jwt; +import org.apache.tomee.microprofile.jwt.config.ConfigurableJWTAuthContextInfo; import org.apache.tomee.microprofile.jwt.config.JWTAuthContextInfo; import org.apache.tomee.microprofile.jwt.principal.JWTCallerPrincipalFactory; import org.eclipse.microprofile.jwt.JsonWebToken; @@ -41,6 +42,7 @@ import java.security.Principal; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Locale; +import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; import java.util.function.Function; @@ -49,10 +51,6 @@ import java.util.stream.Collectors; // async is supported because we only need to do work on the way in @WebFilter(asyncSupported = true, urlPatterns = "/*") public class MPJWTFilter implements Filter { - - @Inject - private Instance<JWTAuthContextInfo> authContextInfo; - @Override public void init(final FilterConfig filterConfig) throws ServletException { // nothing so far @@ -60,7 +58,8 @@ public class MPJWTFilter implements Filter { @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { - if (authContextInfo.isUnsatisfied()) { + final Optional<JWTAuthContextInfo> authContextInfo = getAuthContextInfo(); + if (!authContextInfo.isPresent()) { chain.doFilter(request,response); return; } @@ -91,6 +90,19 @@ public class MPJWTFilter implements Filter { // nothing to do } + @Inject + private Instance<JWTAuthContextInfo> authContextInfo; + @Inject + private ConfigurableJWTAuthContextInfo configurableJWTAuthContextInfo; + + private Optional<JWTAuthContextInfo> getAuthContextInfo() { + if (!authContextInfo.isUnsatisfied()) { + return Optional.of(authContextInfo.get()); + } + + return configurableJWTAuthContextInfo.getJWTAuthContextInfo(); + } + private static Function<HttpServletRequest, JsonWebToken> token(final HttpServletRequest httpServletRequest, final JWTAuthContextInfo authContextInfo) { return new Function<HttpServletRequest, JsonWebToken>() { http://git-wip-us.apache.org/repos/asf/tomee/blob/458da041/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTCDIExtension.java ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTCDIExtension.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTCDIExtension.java index f93fab4..0d7b95a 100644 --- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTCDIExtension.java +++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTCDIExtension.java @@ -19,6 +19,7 @@ package org.apache.tomee.microprofile.jwt.cdi; import org.apache.openejb.loader.SystemInstance; import org.apache.tomee.microprofile.jwt.MPJWTFilter; import org.apache.tomee.microprofile.jwt.MPJWTInitializer; +import org.apache.tomee.microprofile.jwt.config.ConfigurableJWTAuthContextInfo; import org.apache.tomee.microprofile.jwt.jaxrs.MPJWPProviderRegistration; import org.eclipse.microprofile.jwt.Claim; @@ -96,6 +97,7 @@ public class MPJWTCDIExtension implements Extension { } public void observeBeforeBeanDiscovery(@Observes final BeforeBeanDiscovery bbd, final BeanManager beanManager) { + bbd.addAnnotatedType(beanManager.createAnnotatedType(ConfigurableJWTAuthContextInfo.class)); bbd.addAnnotatedType(beanManager.createAnnotatedType(JsonbProducer.class)); bbd.addAnnotatedType(beanManager.createAnnotatedType(MPJWTFilter.class)); bbd.addAnnotatedType(beanManager.createAnnotatedType(MPJWTInitializer.class)); http://git-wip-us.apache.org/repos/asf/tomee/blob/458da041/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/ConfigurableJWTAuthContextInfo.java ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/ConfigurableJWTAuthContextInfo.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/ConfigurableJWTAuthContextInfo.java new file mode 100644 index 0000000..59af1d6 --- /dev/null +++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/ConfigurableJWTAuthContextInfo.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomee.microprofile.jwt.config; + +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.jwt.config.Names; + +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.spi.DeploymentException; +import javax.inject.Inject; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.Optional; + +@RequestScoped +public class ConfigurableJWTAuthContextInfo { + @Inject + private Config config; + + public Optional<JWTAuthContextInfo> getJWTAuthContextInfo() { + final Optional<String> publicKey = config.getOptionalValue(Names.VERIFIER_PUBLIC_KEY, String.class); + final Optional<String> issuer = config.getOptionalValue(Names.ISSUER, String.class); + + if (publicKey.isPresent()) { + final Optional<RSAPublicKey> rsaPublicKey = parsePCKS8(publicKey.get()); + if (rsaPublicKey.isPresent()) { + return Optional.of(new JWTAuthContextInfo(rsaPublicKey.get(), issuer.orElse(""))); + } + } + + return Optional.empty(); + } + + private Optional<RSAPublicKey> parsePCKS8(final String publicKey) { + isPrivatePCKS8(publicKey); + try { + final X509EncodedKeySpec spec = new X509EncodedKeySpec(normalizeAndDecodePCKS8(publicKey)); + final KeyFactory kf = KeyFactory.getInstance("RSA"); + return Optional.of((RSAPublicKey) kf.generatePublic(spec)); + } catch (final NoSuchAlgorithmException | InvalidKeySpecException e) { + return Optional.empty(); + } + } + + private void isPrivatePCKS8(final String publicKey) { + if (publicKey.contains("PRIVATE KEY")) { + throw new DeploymentException("MicroProfile JWT Public Key is Private."); + } + } + + private byte[] normalizeAndDecodePCKS8(final String publicKey) { + final String normalizedKey = + publicKey.replaceAll("-----BEGIN (.*)-----", "") + .replaceAll("-----END (.*)----", "") + .replaceAll("\r\n", "") + .replaceAll("\n", ""); + + return Base64.getDecoder().decode(normalizedKey); + } +} http://git-wip-us.apache.org/repos/asf/tomee/blob/458da041/tck/microprofile-tck/jwt/src/test/java/org/apache/tomee/microprofile/jwt/AppDeploymentExtension.java ---------------------------------------------------------------------- diff --git a/tck/microprofile-tck/jwt/src/test/java/org/apache/tomee/microprofile/jwt/AppDeploymentExtension.java b/tck/microprofile-tck/jwt/src/test/java/org/apache/tomee/microprofile/jwt/AppDeploymentExtension.java index f5f2183..8b9df7f 100644 --- a/tck/microprofile-tck/jwt/src/test/java/org/apache/tomee/microprofile/jwt/AppDeploymentExtension.java +++ b/tck/microprofile-tck/jwt/src/test/java/org/apache/tomee/microprofile/jwt/AppDeploymentExtension.java @@ -3,6 +3,7 @@ package org.apache.tomee.microprofile.jwt; import com.nimbusds.jose.JWSSigner; import org.apache.openejb.loader.JarLocation; import org.eclipse.microprofile.jwt.tck.TCKConstants; +import org.eclipse.microprofile.jwt.tck.config.PublicKeyAsPEMTest; import org.eclipse.microprofile.jwt.tck.util.TokenUtils; import org.jboss.arquillian.container.spi.client.deployment.DeploymentDescription; import org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator; @@ -11,14 +12,15 @@ import org.jboss.arquillian.container.test.spi.client.deployment.DeploymentScena import org.jboss.arquillian.core.spi.LoadableExtension; import org.jboss.arquillian.test.spi.TestClass; import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ArchivePath; import org.jboss.shrinkwrap.api.Node; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; -import java.net.URL; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.logging.Logger; public class AppDeploymentExtension implements LoadableExtension { @@ -71,7 +73,19 @@ public class AppDeploymentExtension implements LoadableExtension { final WebArchive war = WebArchive.class.cast(appArchive); war.addClass(JWTAuthContextInfoProvider.class); + // MP Config in wrong place - See https://github.com/eclipse/microprofile/issues/46. + final Map<ArchivePath, Node> content = + war.getContent( + object -> object.get().matches(".*META-INF/.*")); + content.forEach((archivePath, node) -> war.addAsResource(node.getAsset(), node.getPath())); + + // Spec says that vendor specific ways to load the keys take precedence, so we need to remove it in test + // cases that use the Config approach. + if (testClass.getJavaClass().equals(PublicKeyAsPEMTest.class)) { + war.deleteClass(JWTAuthContextInfoProvider.class); + } + log.info("Augmented war: \n"+war.toString(true)); } } -} \ No newline at end of file +}
