You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jl...@apache.org on 2018/04/16 22:45:20 UTC
[16/38] tomee git commit: Add more implementations and TCK coverage
Add more implementations and TCK coverage
Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/830b3729
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/830b3729
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/830b3729
Branch: refs/heads/master
Commit: 830b372981330bf028b576b155e497b24c85bf9a
Parents: 9c69190
Author: Jean-Louis Monteiro <je...@gmail.com>
Authored: Tue Feb 27 09:16:44 2018 +0100
Committer: Jean-Louis Monteiro <je...@gmail.com>
Committed: Tue Feb 27 09:16:44 2018 +0100
----------------------------------------------------------------------
tck/mp-jwt-embedded/pom.xml | 11 +
.../tomee/microprofile/jwt/ClaimLiteral.java | 32 ++
.../jwt/ClaimProviderBeanAttributes.java | 83 ++++
.../microprofile/jwt/ClaimValueProducer.java | 75 ++++
.../microprofile/jwt/ClaimValueWrapper.java | 54 +++
.../jwt/JWTAuthContextInfoProvider.java | 59 +++
.../jwt/JWTCallerPrincipalFactory.java | 2 +-
.../microprofile/jwt/JsonValueProducer.java | 111 ++++++
.../apache/tomee/microprofile/jwt/KeyUtils.java | 84 ++++
.../microprofile/jwt/MPJWTCDIExtension.java | 391 +++++++++++++++++++
.../tomee/microprofile/jwt/MPJWTContext.java | 9 +-
.../tomee/microprofile/jwt/MPJWTFilter.java | 25 +-
.../microprofile/jwt/MPJWTInitializer.java | 7 +-
.../tomee/microprofile/jwt/MPJWTProducer.java | 196 ++++++++++
.../microprofile/jwt/RawClaimTypeProducer.java | 69 ++++
.../javax.enterprise.inject.spi.Extension | 1 +
...e.microprofile.jwt.JWTCallerPrincipalFactory | 1 +
...file.jwt.principal.JWTCallerPrincipalFactory | 1 -
.../jwt/AppDeploymentExtension.java | 37 ++
....jboss.arquillian.core.spi.LoadableExtension | 1 +
tck/mp-jwt-embedded/src/test/resources/dev.xml | 8 +-
21 files changed, 1237 insertions(+), 20 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/pom.xml
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/pom.xml b/tck/mp-jwt-embedded/pom.xml
index ddbccec..272f4ad 100644
--- a/tck/mp-jwt-embedded/pom.xml
+++ b/tck/mp-jwt-embedded/pom.xml
@@ -112,6 +112,16 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.6.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>JwSecTest
+ </configuration>
+ </plugin>
+ <!--
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
@@ -136,6 +146,7 @@
</execution>
</executions>
</plugin>
+ -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimLiteral.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimLiteral.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimLiteral.java
new file mode 100644
index 0000000..5471b2e
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimLiteral.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+import javax.enterprise.util.AnnotationLiteral;
+
+import org.eclipse.microprofile.jwt.Claim;
+import org.eclipse.microprofile.jwt.Claims;
+
+public class ClaimLiteral extends AnnotationLiteral<Claim> implements Claim {
+ public String value() {
+ return "";
+ }
+
+ public Claims standard() {
+ return Claims.UNKNOWN;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimProviderBeanAttributes.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimProviderBeanAttributes.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimProviderBeanAttributes.java
new file mode 100644
index 0000000..ce6e97c
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimProviderBeanAttributes.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.BeanAttributes;
+
+/**
+ * An implementation of BeanAttributes<Object> that wraps the generic producer BeanAttributes
+ * to allow the MPJWTExtension to collect the types of all corresponding injection sites
+ *
+ */
+public class ClaimProviderBeanAttributes implements BeanAttributes<Object> {
+ /**
+ * Decorate the ConfigPropertyProducer BeanAttributes to set the types the producer applies to. This set is collected
+ * from all injection points annotated with @ConfigProperty.
+ *
+ * @param delegate - the original producer method BeanAttributes
+ * @param types - the full set of @Claim injection point types
+ */
+ public ClaimProviderBeanAttributes(BeanAttributes<Object> delegate, Set<Type> types, Set<Annotation> qualifiers) {
+ this.delegate = delegate;
+ this.types = types;
+ this.qualifiers = qualifiers;
+ if (types.size() == 0) {
+ Thread.dumpStack();
+ }
+ }
+
+ @Override
+ public Set<Type> getTypes() {
+ return types;
+ }
+
+ @Override
+ public Set<Annotation> getQualifiers() {
+ return qualifiers;
+ }
+
+ @Override
+ public Class<? extends Annotation> getScope() {
+ return delegate.getScope();
+ }
+
+ @Override
+ public String getName() {
+ return delegate.getName();
+ }
+
+ @Override
+ public Set<Class<? extends Annotation>> getStereotypes() {
+ return delegate.getStereotypes();
+ }
+
+ @Override
+ public boolean isAlternative() {
+ return delegate.isAlternative();
+ }
+
+ private BeanAttributes<Object> delegate;
+
+ private Set<Type> types;
+
+ private Set<Annotation> qualifiers;
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimValueProducer.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimValueProducer.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimValueProducer.java
new file mode 100644
index 0000000..ae2338f
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimValueProducer.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Optional;
+
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.InjectionPoint;
+
+import org.eclipse.microprofile.jwt.Claim;
+import org.eclipse.microprofile.jwt.ClaimValue;
+import org.eclipse.microprofile.jwt.Claims;
+
+/**
+ * A producer for the ClaimValue<T> wrapper injection sites.
+ * @param <T> the raw claim type
+ */
+public class ClaimValueProducer<T> {
+
+ @Produces
+ @Claim("")
+ ClaimValue<T> produce(InjectionPoint ip) {
+ String name = getName(ip);
+ ClaimValue<Optional<T>> cv = MPJWTProducer.generalClaimValueProducer(name);
+ ClaimValue<T> returnValue = (ClaimValue<T>) cv;
+ Optional<T> value = cv.getValue();
+ // Pull out the ClaimValue<T> T type,
+ Type matchType = ip.getType();
+ Type actualType = Object.class;
+ boolean isOptional = false;
+ if (matchType instanceof ParameterizedType) {
+ actualType = ((ParameterizedType) matchType).getActualTypeArguments()[0];
+ isOptional = matchType.getTypeName().equals(Optional.class.getTypeName());
+ if (isOptional) {
+ actualType = ((ParameterizedType) matchType).getActualTypeArguments()[0];
+ }
+ }
+
+ if (!actualType.getTypeName().startsWith(Optional.class.getTypeName())) {
+ T nestedValue = value.orElse(null);
+ ClaimValueWrapper<T> wrapper = new ClaimValueWrapper<>(cv.getName());
+ wrapper.setValue(nestedValue);
+ returnValue = wrapper;
+ }
+ return returnValue;
+ }
+
+ String getName(InjectionPoint ip) {
+ String name = null;
+ for (Annotation ann : ip.getQualifiers()) {
+ if (ann instanceof Claim) {
+ Claim claim = (Claim) ann;
+ name = claim.standard() == Claims.UNKNOWN ? claim.value() : claim.standard().name();
+ }
+ }
+ return name;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimValueWrapper.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimValueWrapper.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimValueWrapper.java
new file mode 100644
index 0000000..cc16771
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ClaimValueWrapper.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+import org.eclipse.microprofile.jwt.ClaimValue;
+
+/**
+ * An implementation of the ClaimValue interface
+ *
+ * @param <T> the claim value type
+ */
+public class ClaimValueWrapper<T> implements ClaimValue<T> {
+ private String name;
+
+ private T value;
+
+ public ClaimValueWrapper(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public T getValue() {
+ return value;
+ }
+
+ public void setValue(T value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("ClaimValueWrapper[@%s], name=%s, value[%s]=%s", Integer.toHexString(hashCode()),
+ name, value.getClass(), value);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTAuthContextInfoProvider.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTAuthContextInfoProvider.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTAuthContextInfoProvider.java
new file mode 100644
index 0000000..a173ea2
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTAuthContextInfoProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.inject.Produces;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Optional;
+
+@Dependent
+public class JWTAuthContextInfoProvider {
+
+ @Produces
+ Optional<JWTAuthContextInfo> getOptionalContextInfo() {
+ JWTAuthContextInfo contextInfo = new JWTAuthContextInfo();
+
+ // todo use MP Config to load the configuration
+ contextInfo.setIssuedBy("https://server.example.com");
+ RSAPublicKey pk = null;
+ try {
+ pk = (RSAPublicKey) KeyUtils.decodePublicKey("-----BEGIN RSA PUBLIC KEY-----\n" +
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEq\n" +
+ "Fyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwR\n" +
+ "TYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5e\n" +
+ "UF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9\n" +
+ "AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYn\n" +
+ "sIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9x\n" +
+ "nQIDAQAB\n" +
+ "-----END RSA PUBLIC KEY-----\n");
+
+ } catch (final Exception e) {
+ e.printStackTrace();
+ // todo better handling
+ throw new RuntimeException(e);
+ }
+ contextInfo.setSignerKey(pk);
+
+ return Optional.of(contextInfo);
+ }
+
+ @Produces
+ JWTAuthContextInfo getContextInfo() {
+ return getOptionalContextInfo().get();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipalFactory.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipalFactory.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipalFactory.java
index a64f95a..cb00cc5 100644
--- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipalFactory.java
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipalFactory.java
@@ -83,7 +83,7 @@ public abstract class JWTCallerPrincipalFactory {
if (instance == null) {
ServiceLoader<JWTCallerPrincipalFactory> sl = ServiceLoader.load(JWTCallerPrincipalFactory.class, cl);
- URL u = cl.getResource("/META-INF/services/org.eclipse.microprofile.jwt.principal.JWTCallerPrincipalFactory");
+ URL u = cl.getResource("/META-INF/services/org.apache.tomee.microprofile.jwt.JWTCallerPrincipalFactory");
System.out.printf("JWTCallerPrincipalFactory, cl=%s, u=%s, sl=%s\n", cl, u, sl);
try {
for (JWTCallerPrincipalFactory spi : sl) {
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JsonValueProducer.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JsonValueProducer.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JsonValueProducer.java
new file mode 100644
index 0000000..355b1d5
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JsonValueProducer.java
@@ -0,0 +1,111 @@
+/*
+ * 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;
+
+import org.eclipse.microprofile.jwt.Claim;
+import org.eclipse.microprofile.jwt.Claims;
+
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.InjectionPoint;
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+/**
+ * A producer for JsonValue injection types
+ */
+public class JsonValueProducer {
+ private static Logger log = Logger.getLogger(JsonValueProducer.class.getName());
+
+ @Produces
+ @Claim("")
+ public JsonString getJsonString(InjectionPoint ip) {
+ return getValue(ip);
+ }
+
+ @Produces
+ @Claim("")
+ public Optional<JsonString> getOptionalJsonString(InjectionPoint ip) {
+ return getOptionalValue(ip);
+ }
+
+ @Produces
+ @Claim("")
+ public JsonNumber getJsonNumber(InjectionPoint ip) {
+ return getValue(ip);
+ }
+
+ @Produces
+ @Claim("")
+ public Optional<JsonNumber> getOptionalJsonNumber(InjectionPoint ip) {
+ return getOptionalValue(ip);
+ }
+
+ @Produces
+ @Claim("")
+ public JsonArray getJsonArray(InjectionPoint ip) {
+ return getValue(ip);
+ }
+
+ @Produces
+ @Claim("")
+ public Optional<JsonArray> getOptionalJsonArray(InjectionPoint ip) {
+ return getOptionalValue(ip);
+ }
+
+ @Produces
+ @Claim("")
+ public JsonObject getJsonObject(InjectionPoint ip) {
+ return getValue(ip);
+ }
+
+ @Produces
+ @Claim("")
+ public Optional<JsonObject> getOptionalJsonObject(InjectionPoint ip) {
+ return getOptionalValue(ip);
+ }
+
+ public <T extends JsonValue> T getValue(InjectionPoint ip) {
+ log.fine(String.format("JsonValueProducer(%s).produce", ip));
+ String name = getName(ip);
+ T jsonValue = (T) MPJWTProducer.generalJsonValueProducer(name);
+ return jsonValue;
+ }
+
+ public <T extends JsonValue> Optional<T> getOptionalValue(InjectionPoint ip) {
+ log.fine(String.format("JsonValueProducer(%s).produce", ip));
+ String name = getName(ip);
+ T jsonValue = (T) MPJWTProducer.generalJsonValueProducer(name);
+ return Optional.ofNullable(jsonValue);
+ }
+
+ String getName(InjectionPoint ip) {
+ String name = null;
+ for (Annotation ann : ip.getQualifiers()) {
+ if (ann instanceof Claim) {
+ Claim claim = (Claim) ann;
+ name = claim.standard() == Claims.UNKNOWN ? claim.value() : claim.standard().name();
+ }
+ }
+ return name;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/KeyUtils.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/KeyUtils.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/KeyUtils.java
new file mode 100644
index 0000000..07e00f2
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/KeyUtils.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+public class KeyUtils {
+ public static PrivateKey readPrivateKey(String pemResName) throws Exception {
+ InputStream contentIS = KeyUtils.class.getResourceAsStream(pemResName);
+ byte[] tmp = new byte[4096];
+ int length = contentIS.read(tmp);
+ PrivateKey privateKey = decodePrivateKey(new String(tmp, 0, length));
+ return privateKey;
+ }
+
+ public static PublicKey readPublicKey(String pemResName) throws Exception {
+ InputStream contentIS = KeyUtils.class.getResourceAsStream(pemResName);
+ byte[] tmp = new byte[4096];
+ int length = contentIS.read(tmp);
+ PublicKey publicKey = decodePublicKey(new String(tmp, 0, length));
+ return publicKey;
+ }
+
+ public static KeyPair generateKeyPair(int keySize) throws NoSuchAlgorithmException {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(keySize);
+ KeyPair keyPair = keyPairGenerator.genKeyPair();
+ return keyPair;
+ }
+
+ public static PrivateKey decodePrivateKey(String pemEncoded) throws Exception {
+ pemEncoded = removeBeginEnd(pemEncoded);
+ byte[] pkcs8EncodedBytes = Base64.getDecoder().decode(pemEncoded);
+
+ // extract the private key
+
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ PrivateKey privKey = kf.generatePrivate(keySpec);
+ return privKey;
+ }
+
+ public static PublicKey decodePublicKey(String pemEncoded) throws Exception {
+ pemEncoded = removeBeginEnd(pemEncoded);
+ byte[] encodedBytes = Base64.getDecoder().decode(pemEncoded);
+
+ X509EncodedKeySpec spec = new X509EncodedKeySpec(encodedBytes);
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ return kf.generatePublic(spec);
+ }
+
+ private static String removeBeginEnd(String pem) {
+ pem = pem.replaceAll("-----BEGIN(.*)KEY-----", "");
+ pem = pem.replaceAll("-----END(.*)KEY-----", "");
+ pem = pem.replaceAll("\r\n", "");
+ pem = pem.replaceAll("\n", "");
+ return pem.trim();
+ }
+ private KeyUtils() {
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTCDIExtension.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTCDIExtension.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTCDIExtension.java
new file mode 100644
index 0000000..aa15a33
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTCDIExtension.java
@@ -0,0 +1,391 @@
+/*
+ * 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;
+
+import org.eclipse.microprofile.jwt.Claim;
+import org.eclipse.microprofile.jwt.Claims;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.SessionScoped;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.AfterDeploymentValidation;
+import javax.enterprise.inject.spi.BeanAttributes;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.BeforeBeanDiscovery;
+import javax.enterprise.inject.spi.DeploymentException;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.InjectionPoint;
+import javax.enterprise.inject.spi.ProcessBeanAttributes;
+import javax.enterprise.inject.spi.ProcessInjectionPoint;
+import javax.enterprise.inject.spi.ProcessProducer;
+import javax.inject.Provider;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * A CDI extension that provides a producer for the current authenticated JsonWebToken based on a thread
+ * local value that is managed by the {@link JWTAuthMechanism} request
+ * authentication handler.
+ * <p>
+ * This also installs the producer methods for the discovered:
+ * <ul>
+ * <li>@Claim ClaimValue<T> injection sites.</li>
+ * <li>@Claim raw type<T> injection sites.</li>
+ * <li>@Claim JsonValue injection sites.</li>
+ * </ul>
+ *
+ * @see JWTAuthMechanism
+ */
+public class MPJWTCDIExtension implements Extension {
+ private static Logger log = Logger.getLogger(MPJWTCDIExtension.class.getName());
+
+ /**
+ * Register the MPJWTProducer JsonWebToken producer bean
+ *
+ * @param bbd before discovery event
+ * @param beanManager cdi bean manager
+ */
+ public void observeBeforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd, BeanManager beanManager) {
+ log.fine(String.format("MPJWTExtension(), added JWTPrincipalProducer"));
+ bbd.addAnnotatedType(beanManager.createAnnotatedType(TCKTokenParser.class));
+ bbd.addAnnotatedType(beanManager.createAnnotatedType(MPJWTFilter.class));
+ bbd.addAnnotatedType(beanManager.createAnnotatedType(MPJWTInitializer.class));
+ bbd.addAnnotatedType(beanManager.createAnnotatedType(JWTAuthContextInfoProvider.class));
+ bbd.addAnnotatedType(beanManager.createAnnotatedType(MPJWTProducer.class));
+ bbd.addAnnotatedType(beanManager.createAnnotatedType(RawClaimTypeProducer.class));
+ bbd.addAnnotatedType(beanManager.createAnnotatedType(ClaimValueProducer.class));
+ bbd.addAnnotatedType(beanManager.createAnnotatedType(JsonValueProducer.class));
+ }
+
+ /**
+ * Replace the general producer method BeanAttributes with one bound to the collected injection site
+ * types to properly reflect all of the type locations the producer method applies to.
+ *
+ * @param pba the ProcessBeanAttributes
+ * @see ClaimProviderBeanAttributes
+ */
+ public void addTypeToClaimProducer(@Observes ProcessBeanAttributes pba) {
+ if (pba.getAnnotated().isAnnotationPresent(Claim.class)) {
+ Claim claim = pba.getAnnotated().getAnnotation(Claim.class);
+ if (claim.value().length() == 0 && claim.standard() == Claims.UNKNOWN) {
+ log.fine(String.format("addTypeToClaimProducer: %s\n", pba.getAnnotated()));
+ BeanAttributes delegate = pba.getBeanAttributes();
+ String name = delegate.getName();
+ if (delegate.getTypes().contains(Optional.class)) {
+ if (providerOptionalTypes.size() == 0) {
+ providerOptionalTypes.add(Optional.class);
+ }
+ pba.setBeanAttributes(new ClaimProviderBeanAttributes(delegate, providerOptionalTypes, providerQualifiers));
+ // This is
+ } else if (name != null && name.startsWith("RawClaimTypeProducer#")) {
+ if (rawTypes.size() == 0) {
+ rawTypes.add(Object.class);
+ }
+ pba.setBeanAttributes(new ClaimProviderBeanAttributes(delegate, rawTypes, rawTypeQualifiers));
+ log.fine(String.format("Setup RawClaimTypeProducer BeanAttributes"));
+ }
+ }
+ }
+ }
+
+ public void afterDeploymentValidation(@Observes AfterDeploymentValidation event, BeanManager beanManager) {
+ }
+
+ void doProcessProducers(@Observes ProcessProducer pp) {
+ }
+
+ /**
+ * Handle the non-{@linkplain Provider}, {@linkplain org.eclipse.microprofile.jwt.ClaimValue}, and
+ * {@linkplain javax.json.JsonValue} claim injection types.
+ *
+ * @param pip - the injection point event information
+ * @see RawClaimTypeProducer
+ */
+ void processClaimInjections(@Observes ProcessInjectionPoint pip) {
+ log.fine(String.format("pipRaw: %s", pip.getInjectionPoint()));
+ InjectionPoint ip = pip.getInjectionPoint();
+ if (ip.getAnnotated().isAnnotationPresent(Claim.class)) {
+ Claim claim = ip.getAnnotated().getAnnotation(Claim.class);
+ if (ip.getType() instanceof Class) {
+ Class rawClass = (Class) ip.getType();
+ // Primative types
+ if (Modifier.isFinal(rawClass.getModifiers())) {
+ rawTypes.add(ip.getType());
+ rawTypeQualifiers.add(claim);
+ log.fine(String.format("+++ Added Claim raw type: %s", ip.getType()));
+ Class declaringClass = ip.getMember().getDeclaringClass();
+ Annotation[] appScoped = declaringClass.getAnnotationsByType(ApplicationScoped.class);
+ Annotation[] sessionScoped = declaringClass.getAnnotationsByType(SessionScoped.class);
+ if ((appScoped != null && appScoped.length > 0) || (sessionScoped != null && sessionScoped.length > 0)) {
+ String err = String.format("A raw type cannot be injected into application/session scope: IP=%s", ip);
+ pip.addDefinitionError(new DeploymentException(err));
+ }
+ }
+ // This handles collections of primative types
+ } else if (isRawParameterizedType(ip.getType())) {
+ log.fine(String.format("+++ Added Claim ParameterizedType: %s", ip.getType()));
+ rawTypes.add(ip.getType());
+ rawTypeQualifiers.add(claim);
+ }
+ } else {
+ log.fine(String.format("Skipping pip: %s, type: %s/%s", ip, ip.getType(), ip.getType().getClass()));
+ }
+ }
+
+ /**
+ * Collect the types of all {@linkplain Provider} injection points annotated with {@linkplain Claim}.
+ *
+ * @param pip - the injection point event information
+ */
+ void processClaimProviderInjections(@Observes ProcessInjectionPoint<?, ? extends Provider> pip) {
+ log.fine(String.format("pip: %s", pip.getInjectionPoint()));
+ final InjectionPoint ip = pip.getInjectionPoint();
+ if (ip.getAnnotated().isAnnotationPresent(Claim.class)) {
+ Claim claim = ip.getAnnotated().getAnnotation(Claim.class);
+ if (claim.value().length() == 0 && claim.standard() == Claims.UNKNOWN) {
+ pip.addDefinitionError(new DeploymentException("@Claim at: " + ip + " has no name or valid standard enum setting"));
+ }
+ boolean usesEnum = claim.standard() != Claims.UNKNOWN;
+ final String claimName = usesEnum ? claim.standard().name() : claim.value();
+ log.fine(String.format("Checking Provider Claim(%s), ip: %s", claimName, ip));
+ ClaimIP claimIP = claims.get(claimName);
+ Type matchType = ip.getType();
+ // The T from the Provider<T> injection site
+ Type actualType = ((ParameterizedType) matchType).getActualTypeArguments()[0];
+ // Don't add Optional or JsonValue as this is handled specially
+ if (!optionalOrJsonValue(actualType)) {
+ rawTypes.add(actualType);
+ } else if (!actualType.getTypeName().startsWith("javax.json.Json")) {
+ // Validate that this is not an Optional<JsonValue>
+ Type innerType = ((ParameterizedType) actualType).getActualTypeArguments()[0];
+ if (!innerType.getTypeName().startsWith("javax.json.Json")) {
+ providerOptionalTypes.add(actualType);
+ providerQualifiers.add(claim);
+ }
+ }
+ rawTypeQualifiers.add(claim);
+ ClaimIPType key = new ClaimIPType(claimName, actualType);
+ if (claimIP == null) {
+ claimIP = new ClaimIP(actualType, actualType, false, claim);
+ claimIP.setProviderSite(true);
+ claims.put(key, claimIP);
+ }
+ claimIP.getInjectionPoints().add(ip);
+ log.fine(String.format("+++ Added Provider Claim(%s) ip: %s", claimName, ip));
+
+ }
+ }
+
+ /**
+ * Create producer methods for each ClaimValue injection site
+ *
+ * @param event - AfterBeanDiscovery
+ * @param beanManager - CDI bean manager
+ */
+ void observesAfterBeanDiscovery(@Observes final AfterBeanDiscovery event, final BeanManager beanManager) {
+ log.fine(String.format("observesAfterBeanDiscovery, %s", claims));
+ installClaimValueProducerMethodsViaSyntheticBeans(event, beanManager);
+
+ //installClaimValueProducesViaTemplateType(event, beanManager);
+ }
+
+ /**
+ * Create a synthetic bean with a custom Producer for the non-Provider injection sites.
+ *
+ * @param event - AfterBeanDiscovery
+ * @param beanManager - CDI bean manager
+ */
+ private void installClaimValueProducerMethodsViaSyntheticBeans(final AfterBeanDiscovery event, final BeanManager beanManager) {
+
+ }
+
+ private boolean optionalOrJsonValue(Type type) {
+ boolean isOptionOrJson = type.getTypeName().startsWith(Optional.class.getTypeName())
+ | type.getTypeName().startsWith("javax.json.Json");
+ return isOptionOrJson;
+ }
+
+ private boolean isRawParameterizedType(Type type) {
+ boolean isRawParameterizedType = false;
+ if (type instanceof ParameterizedType) {
+ ParameterizedType ptype = ParameterizedType.class.cast(type);
+ Type rawType = ptype.getRawType();
+ String rawTypeName = rawType.getTypeName();
+ isRawParameterizedType = !rawTypeName.startsWith("org.eclipse.microprofile.jwt");
+ }
+ return isRawParameterizedType;
+ }
+
+ /**
+ * A map of claim,type pairs to the injection site information
+ */
+ private HashMap<ClaimIPType, ClaimIP> claims = new HashMap<>();
+
+ private Set<Type> providerOptionalTypes = new HashSet<>();
+
+ private Set<Type> providerTypes = new HashSet<>();
+
+ private Set<Type> rawTypes = new HashSet<>();
+
+ private Set<Annotation> rawTypeQualifiers = new HashSet<>();
+
+ private Set<Annotation> providerQualifiers = new HashSet<>();
+
+ /**
+ * A key for a claim,injection site type pair
+ */
+ public static class ClaimIPType implements Comparable<ClaimIPType> {
+ public ClaimIPType(String claimName, Type ipType) {
+ this.claimName = claimName;
+ this.ipType = ipType;
+ }
+
+ /**
+ * Order the @Claim ClaimValue<T> on the @Claim.value and then T type name
+ *
+ * @param o - ClaimIP to compare to
+ * @return the ordering of this claim relative to o
+ */
+ @Override
+ public int compareTo(ClaimIPType o) {
+ int compareTo = claimName.compareTo(o.claimName);
+ if (compareTo == 0) {
+ compareTo = ipType.getTypeName().compareTo(o.ipType.getTypeName());
+ }
+ return compareTo;
+ }
+
+ private String claimName;
+
+ private Type ipType;
+ }
+
+ /**
+ * The representation of an @Claim annotated injection site
+ */
+ public static class ClaimIP {
+ /**
+ * Create a ClaimIP from the injection site information
+ *
+ * @param matchType - the outer type of the injection site
+ * @param valueType - the parameterized type of the injection site
+ * @param isOptional - is the injection site an Optional
+ * @param claim - the Claim qualifier
+ */
+ public ClaimIP(Type matchType, Type valueType, boolean isOptional, Claim claim) {
+ this.matchType = matchType;
+ this.valueType = valueType;
+ this.claim = claim;
+ }
+
+ public Type getMatchType() {
+ return matchType;
+ }
+
+ public String getClaimName() {
+ return claim.standard() == Claims.UNKNOWN ? claim.value() : claim.standard().name();
+ }
+
+ public Claim getClaim() {
+ return claim;
+ }
+
+ public Type getValueType() {
+ return valueType;
+ }
+
+ public boolean isOptional() {
+ return isOptional;
+ }
+
+ public boolean isProviderSite() {
+ return isProviderSite;
+ }
+
+ public void setProviderSite(boolean providerSite) {
+ this.isProviderSite = providerSite;
+ }
+
+ public boolean isNonStandard() {
+ return isNonStandard;
+ }
+
+ public void setNonStandard(boolean nonStandard) {
+ isNonStandard = nonStandard;
+ }
+
+ public boolean isJsonValue() {
+ return isJsonValue;
+ }
+
+ public void setJsonValue(boolean jsonValue) {
+ isJsonValue = jsonValue;
+ }
+
+ public Set<InjectionPoint> getInjectionPoints() {
+ return injectionPoints;
+ }
+
+ @Override
+ public String toString() {
+ return "ClaimIP{" +
+ "type=" + matchType +
+ ", claim=" + claim +
+ ", ips=" + injectionPoints +
+ '}';
+ }
+
+ /**
+ * The injection site value type
+ */
+ private Type matchType;
+
+ /**
+ * The actual type of of the ParameterizedType matchType
+ */
+ private Type valueType;
+
+ /**
+ * Is valueType actually wrapped in an Optional
+ */
+ private boolean isOptional;
+
+ private boolean isProviderSite;
+
+ private boolean isNonStandard;
+
+ private boolean isJsonValue;
+
+ /**
+ * The injection site @Claim annotation value
+ */
+ private Claim claim;
+
+ /**
+ * The location that share the @Claim/type combination
+ */
+ private HashSet<InjectionPoint> injectionPoints = new HashSet<>();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTContext.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTContext.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTContext.java
index bffccf2..07dfe0b 100644
--- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTContext.java
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTContext.java
@@ -27,12 +27,11 @@ import java.util.function.Predicate;
/**
* Responsible for holding the runtime model
*/
-@ApplicationScoped
public class MPJWTContext {
- private final ConcurrentMap<MPJWTConfigKey, MPJWTConfigValue> configuration = new ConcurrentHashMap<>();
+ private static final ConcurrentMap<MPJWTConfigKey, MPJWTConfigValue> configuration = new ConcurrentHashMap<>();
- public MPJWTConfigValue addMapping(final MPJWTConfigKey key, final MPJWTConfigValue value) {
+ public static MPJWTConfigValue addMapping(final MPJWTConfigKey key, final MPJWTConfigValue value) {
Objects.requireNonNull(key, "MP JWT Key is required");
Objects.requireNonNull(value, "MP JWT Value is required");
@@ -44,12 +43,12 @@ public class MPJWTContext {
return value;
}
- public Optional<MPJWTConfigValue> get(final MPJWTConfigKey key) {
+ public static Optional<MPJWTConfigValue> get(final MPJWTConfigKey key) {
Objects.requireNonNull(key, "MP JWT Key is required to retrieve the configuration");
return Optional.ofNullable(configuration.get(key));
}
- public Optional<Map.Entry<MPJWTConfigKey, MPJWTConfigValue>> findFirst(final String path) {
+ public static Optional<Map.Entry<MPJWTConfigKey, MPJWTConfigValue>> findFirst(final String path) {
return configuration.entrySet()
.stream()
.filter(new Predicate<ConcurrentMap.Entry<MPJWTConfigKey, MPJWTConfigValue>>() {
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
index 092ad9d..a8f7cb4 100644
--- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
@@ -38,9 +38,6 @@ import java.util.Optional;
public class MPJWTFilter implements Filter {
@Inject
- private MPJWTContext context;
-
- @Inject
private JWTAuthContextInfo authContextInfo;
@Override
@@ -54,16 +51,31 @@ public class MPJWTFilter implements Filter {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final Optional<Map.Entry<MPJWTContext.MPJWTConfigKey, MPJWTContext.MPJWTConfigValue>> first =
- context.findFirst(httpServletRequest.getRequestURI());
+ MPJWTContext.findFirst(httpServletRequest.getRequestURI());
- if (first.isPresent()) { // nothing found in the context
+ if (!first.isPresent()) { // nothing found in the context
chain.doFilter(request, response);
+ return;
}
// todo get JWT and do validation
// todo not sure what to do with the realm
- final JsonWebToken jsonWebToken = null; // will be build during validation
+ final String authorizationHeader = ((HttpServletRequest) request).getHeader("Authorization");
+ final String token = authorizationHeader.substring("bearer ".length());
+ final JsonWebToken jsonWebToken;
+ try {
+ jsonWebToken = DefaultJWTCallerPrincipalFactory.instance().parse(token, authContextInfo);
+
+ } catch (final ParseException e) {
+ // todo properly handle the exception as required per spec
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+
+ // associate with the producer. Should not be needed.
+ // todo We should be able to retrieve it based on the HTTP Servlet Request in the producer
+ MPJWTProducer.setJWTPrincipal(jsonWebToken);
// now wrap the httpServletRequest and override the principal so CXF can propagate into the SecurityContext
chain.doFilter(new HttpServletRequestWrapper(httpServletRequest) {
@@ -85,6 +97,7 @@ public class MPJWTFilter implements Filter {
}, response);
+
}
@Override
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTInitializer.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTInitializer.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTInitializer.java
index dc3d7ba..bb8bb16 100644
--- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTInitializer.java
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTInitializer.java
@@ -34,9 +34,6 @@ import java.util.Set;
@HandlesTypes(LoginConfig.class)
public class MPJWTInitializer implements ServletContainerInitializer {
- @Inject
- private MPJWTContext context;
-
@Override
public void onStartup(final Set<Class<?>> classes, final ServletContext ctx) throws ServletException {
@@ -59,11 +56,13 @@ public class MPJWTInitializer implements ServletContainerInitializer {
final FilterRegistration.Dynamic mpJwtFilter = ctx.addFilter("mp-jwt-filter", MPJWTFilter.class);
mpJwtFilter.setAsyncSupported(true);
+ mpJwtFilter.addMappingForUrlPatterns(null, false, "/*");
- context.addMapping(
+ MPJWTContext.addMapping(
new MPJWTContext.MPJWTConfigKey(
ctx.getContextPath(),
applicationPath == null ? "" : applicationPath.value()),
+
new MPJWTContext.MPJWTConfigValue(
loginConfig.authMethod(),
loginConfig.realmName())
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTProducer.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTProducer.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTProducer.java
new file mode 100644
index 0000000..195f323
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTProducer.java
@@ -0,0 +1,196 @@
+/*
+ * 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;
+
+import org.eclipse.microprofile.jwt.ClaimValue;
+import org.eclipse.microprofile.jwt.JsonWebToken;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Destroyed;
+import javax.enterprise.context.Initialized;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Produces;
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+/**
+ * A class that tracks the current validated MP-JWT and associated JsonWebToken via a thread
+ * local to provide a @RequestScoped JsonWebToken producer method.
+ * <p>
+ * It also provides utility methods for access the current JsonWebToken claim values.
+ */
+@ApplicationScoped
+public class MPJWTProducer {
+ private static Logger log = Logger.getLogger(MPJWTProducer.class.getName());
+ private static final String TMP = "tmp";
+ private static ThreadLocal<JsonWebToken> currentPrincipal = new ThreadLocal<>();
+
+ public static void setJWTPrincipal(JsonWebToken principal) {
+ currentPrincipal.set(principal);
+ }
+
+ public static JsonWebToken getJWTPrincpal() {
+ return currentPrincipal.get();
+ }
+
+ @PostConstruct
+ void init() {
+ log.fine("MPJWTProducer initialized");
+ }
+
+ void observeRequestInitialized(@Observes @Initialized(RequestScoped.class) Object event) {
+ log.finest(String.format("observeRequestInitialized, event=%s", event));
+ }
+
+ void observeRequestDestroyed(@Observes @Destroyed(RequestScoped.class) Object event) {
+ log.finest(String.format("observeRequestDestroyed, event=%s", event));
+ }
+
+ /**
+ * The @RequestScoped producer method for the current JsonWebToken
+ *
+ * @return
+ */
+ @Produces
+ @RequestScoped
+ JsonWebToken currentPrincipalOrNull() {
+ return currentPrincipal.get();
+ }
+
+ /**
+ * A utility method for accessing a claim from the current JsonWebToken as a ClaimValue<Optional<T>> object.
+ *
+ * @param name - name of the claim
+ * @param <T> expected actual type of the claim
+ * @return the claim value wrapper object
+ */
+ static <T> ClaimValue<Optional<T>> generalClaimValueProducer(String name) {
+ ClaimValueWrapper<Optional<T>> wrapper = new ClaimValueWrapper<>(name);
+ T value = getValue(name, false);
+ Optional<T> optValue = Optional.ofNullable(value);
+ wrapper.setValue(optValue);
+ return wrapper;
+ }
+
+ /**
+ * Return the indicated claim value as a JsonValue
+ *
+ * @param name - name of the claim
+ * @return a JsonValue wrapper
+ */
+ static JsonValue generalJsonValueProducer(String name) {
+ Object value = getValue(name, false);
+ JsonValue jsonValue = wrapValue(value);
+ return jsonValue;
+ }
+
+ public static <T> T getValue(String name, boolean isOptional) {
+ JsonWebToken jwt = getJWTPrincpal();
+ if (jwt == null) {
+ log.fine(String.format("getValue(%s), null JsonWebToken", name));
+ return null;
+ }
+
+ Optional<T> claimValue = jwt.claim(name);
+ if (!isOptional && !claimValue.isPresent()) {
+ log.fine(String.format("Failed to find Claim for: %s", name));
+ }
+ log.fine(String.format("getValue(%s), isOptional=%s, claimValue=%s", name, isOptional, claimValue));
+ return claimValue.orElse(null);
+ }
+
+ static JsonObject replaceMap(Map<String, Object> map) {
+ JsonObjectBuilder builder = Json.createObjectBuilder();
+ for (Map.Entry<String, Object> entry : map.entrySet()) {
+ Object entryValue = entry.getValue();
+ if (entryValue instanceof Map) {
+ JsonObject entryJsonObject = replaceMap((Map<String, Object>) entryValue);
+ builder.add(entry.getKey(), entryJsonObject);
+ } else if (entryValue instanceof List) {
+ JsonArray array = (JsonArray) wrapValue(entryValue);
+ builder.add(entry.getKey(), array);
+ } else if (entryValue instanceof Long || entryValue instanceof Integer) {
+ long lvalue = ((Number) entryValue).longValue();
+ builder.add(entry.getKey(), lvalue);
+ } else if (entryValue instanceof Double || entryValue instanceof Float) {
+ double dvalue = ((Number) entryValue).doubleValue();
+ builder.add(entry.getKey(), dvalue);
+ } else if (entryValue instanceof Boolean) {
+ boolean flag = ((Boolean) entryValue).booleanValue();
+ builder.add(entry.getKey(), flag);
+ } else if (entryValue instanceof String) {
+ builder.add(entry.getKey(), entryValue.toString());
+ }
+ }
+ return builder.build();
+ }
+
+ static JsonValue wrapValue(Object value) {
+ JsonValue jsonValue = null;
+ if (value instanceof JsonValue) {
+ // This may already be a JsonValue
+ jsonValue = (JsonValue) value;
+ } else if (value instanceof String) {
+ jsonValue = Json.createObjectBuilder()
+ .add(TMP, value.toString())
+ .build()
+ .getJsonString(TMP);
+ } else if (value instanceof Number) {
+ Number number = (Number) value;
+ if ((number instanceof Long) || (number instanceof Integer)) {
+ jsonValue = Json.createObjectBuilder()
+ .add(TMP, number.longValue())
+ .build()
+ .getJsonNumber(TMP);
+ } else {
+ jsonValue = Json.createObjectBuilder()
+ .add(TMP, number.doubleValue())
+ .build()
+ .getJsonNumber(TMP);
+ }
+ } else if (value instanceof Boolean) {
+ Boolean flag = (Boolean) value;
+ jsonValue = flag ? JsonValue.TRUE : JsonValue.FALSE;
+ } else if (value instanceof Collection) {
+ JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+ Collection list = (Collection) value;
+ for (Object element : list) {
+ if (element instanceof String) {
+ arrayBuilder.add(element.toString());
+ } else {
+ JsonValue jvalue = wrapValue(element);
+ arrayBuilder.add(jvalue);
+ }
+ }
+ jsonValue = arrayBuilder.build();
+ } else if (value instanceof Map) {
+ jsonValue = replaceMap((Map) value);
+ }
+ return jsonValue;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/RawClaimTypeProducer.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/RawClaimTypeProducer.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/RawClaimTypeProducer.java
new file mode 100644
index 0000000..b52912c
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/RawClaimTypeProducer.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+import org.eclipse.microprofile.jwt.Claim;
+import org.eclipse.microprofile.jwt.ClaimValue;
+import org.eclipse.microprofile.jwt.Claims;
+
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.InjectionPoint;
+import javax.inject.Named;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+/**
+ *
+ */
+public class RawClaimTypeProducer {
+ private static Logger log = Logger.getLogger(RawClaimTypeProducer.class.getName());
+
+ @Produces
+ @Claim("")
+ @Named("RawClaimTypeProducer#getValue")
+ public Object getValue(InjectionPoint ip) {
+ log.fine(String.format("getValue(%s)", ip));
+ String name = getName(ip);
+ ClaimValue<Optional<Object>> cv = MPJWTProducer.generalClaimValueProducer(name);
+ Optional<Object> value = cv.getValue();
+ Object returnValue = value.orElse(null);
+ return returnValue;
+ }
+
+ @Produces
+ @Claim("")
+ @Named("RawClaimTypeProducer#getOptionalValue")
+ public Optional getOptionalValue(InjectionPoint ip) {
+ log.fine(String.format("getOptionalValue(%s)", ip));
+ String name = getName(ip);
+ ClaimValue<Optional<Object>> cv = MPJWTProducer.generalClaimValueProducer(name);
+ Optional<Object> value = cv.getValue();
+ return value;
+ }
+
+ String getName(InjectionPoint ip) {
+ String name = null;
+ for (Annotation ann : ip.getQualifiers()) {
+ if (ann instanceof Claim) {
+ Claim claim = (Claim) ann;
+ name = claim.standard() == Claims.UNKNOWN ? claim.value() : claim.standard().name();
+ }
+ }
+ return name;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/tck/mp-jwt-embedded/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..5e3bccc
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1 @@
+org.apache.tomee.microprofile.jwt.MPJWTCDIExtension
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/resources/META-INF/services/org.apache.tomee.microprofile.jwt.JWTCallerPrincipalFactory
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/resources/META-INF/services/org.apache.tomee.microprofile.jwt.JWTCallerPrincipalFactory b/tck/mp-jwt-embedded/src/main/resources/META-INF/services/org.apache.tomee.microprofile.jwt.JWTCallerPrincipalFactory
new file mode 100644
index 0000000..67f39db
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/resources/META-INF/services/org.apache.tomee.microprofile.jwt.JWTCallerPrincipalFactory
@@ -0,0 +1 @@
+org.apache.tomee.microprofile.jwt.DefaultJWTCallerPrincipalFactory
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/main/resources/META-INF/services/org.eclipse.microprofile.jwt.principal.JWTCallerPrincipalFactory
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/resources/META-INF/services/org.eclipse.microprofile.jwt.principal.JWTCallerPrincipalFactory b/tck/mp-jwt-embedded/src/main/resources/META-INF/services/org.eclipse.microprofile.jwt.principal.JWTCallerPrincipalFactory
deleted file mode 100644
index 67f39db..0000000
--- a/tck/mp-jwt-embedded/src/main/resources/META-INF/services/org.eclipse.microprofile.jwt.principal.JWTCallerPrincipalFactory
+++ /dev/null
@@ -1 +0,0 @@
-org.apache.tomee.microprofile.jwt.DefaultJWTCallerPrincipalFactory
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/test/java/org/apache/tomee/microprofile/jwt/AppDeploymentExtension.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/test/java/org/apache/tomee/microprofile/jwt/AppDeploymentExtension.java b/tck/mp-jwt-embedded/src/test/java/org/apache/tomee/microprofile/jwt/AppDeploymentExtension.java
new file mode 100644
index 0000000..4ade364
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/test/java/org/apache/tomee/microprofile/jwt/AppDeploymentExtension.java
@@ -0,0 +1,37 @@
+package org.apache.tomee.microprofile.jwt;
+
+import org.jboss.arquillian.container.spi.client.deployment.DeploymentDescription;
+import org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator;
+import org.jboss.arquillian.container.test.spi.client.deployment.DeploymentScenarioGenerator;
+import org.jboss.arquillian.core.spi.LoadableExtension;
+import org.jboss.arquillian.test.spi.TestClass;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+
+import java.util.Collections;
+import java.util.List;
+
+public class AppDeploymentExtension implements LoadableExtension {
+ @Override
+ public void register(final ExtensionBuilder extensionBuilder) {
+ extensionBuilder.service(DeploymentScenarioGenerator.class, SimpleDeploymentScenarioGenerator.class);
+ }
+
+ public static class SimpleDeploymentScenarioGenerator implements DeploymentScenarioGenerator {
+
+ private final DeploymentScenarioGenerator standard = new AnnotationDeploymentScenarioGenerator();
+
+ @Override
+ public List<DeploymentDescription> generate(final TestClass testClass) {
+ final List<DeploymentDescription> stdDeploymentDescriptions = standard.generate(testClass);
+
+ if (stdDeploymentDescriptions != null && !stdDeploymentDescriptions.isEmpty()) {
+ return stdDeploymentDescriptions;
+ }
+
+ return Collections.singletonList(new DeploymentDescription("test.war",
+ ShrinkWrap.create(WebArchive.class, "test.war").add(EmptyAsset.INSTANCE, "WEB-INF/beans.xml")));
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/tck/mp-jwt-embedded/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
new file mode 100644
index 0000000..98a4867
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
@@ -0,0 +1 @@
+org.apache.tomee.microprofile.jwt.AppDeploymentExtension
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/830b3729/tck/mp-jwt-embedded/src/test/resources/dev.xml
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/test/resources/dev.xml b/tck/mp-jwt-embedded/src/test/resources/dev.xml
index 16038c9..f6c4fbd 100644
--- a/tck/mp-jwt-embedded/src/test/resources/dev.xml
+++ b/tck/mp-jwt-embedded/src/test/resources/dev.xml
@@ -41,8 +41,9 @@
</run>
</groups>
<classes>
+ <!-- OK
+ -->
<class name="org.eclipse.microprofile.jwt.tck.parsing.TokenValidationTest" />
- <!--
<class name="org.eclipse.microprofile.jwt.tck.util.TokenUtilsTest" />
<class name="org.eclipse.microprofile.jwt.tck.parsing.TestTokenClaimTypesTest" />
<class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.UnsecuredPingTest" />
@@ -52,10 +53,11 @@
<class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.ProviderInjectionTest" />
<class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.RolesAllowedTest" />
<class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.InvalidTokenTest" />
+ <!-- KO
-->
</classes>
</test>
- <!--
+
<test name="extended-tests" verbose="10">
<groups>
<define name="extended-groups">
@@ -79,5 +81,5 @@
<class name="org.eclipse.microprofile.jwt.tck.container.servlet.ServletTest" />
</classes>
</test>
- -->
+
</suite>