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:17 UTC
[13/38] tomee git commit: Get some code from
https://github.com/MicroProfileJWT/microprofile-jwt-auth-prototype
Get some code from https://github.com/MicroProfileJWT/microprofile-jwt-auth-prototype
Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/c5b38b37
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/c5b38b37
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/c5b38b37
Branch: refs/heads/master
Commit: c5b38b37142af58fe771d7a397b388cec847f734
Parents: d987d3a
Author: Jean-Louis Monteiro <je...@gmail.com>
Authored: Thu Feb 22 22:19:49 2018 +0100
Committer: Jean-Louis Monteiro <je...@gmail.com>
Committed: Thu Feb 22 22:19:49 2018 +0100
----------------------------------------------------------------------
tck/mp-jwt-embedded/pom.xml | 15 +
.../jwt/DefaultJWTCallerPrincipal.java | 305 ++++++++++++++++++-
.../jwt/DefaultJWTCallerPrincipalFactory.java | 86 ++++++
.../microprofile/jwt/JWTAuthContextInfo.java | 66 ++++
.../microprofile/jwt/JWTCallerPrincipal.java | 9 +-
.../jwt/JWTCallerPrincipalFactory.java | 124 ++++++++
.../tomee/microprofile/jwt/MPJWTContext.java | 3 -
.../tomee/microprofile/jwt/MPJWTFilter.java | 10 +-
.../tomee/microprofile/jwt/ParseException.java | 32 ++
...file.jwt.principal.JWTCallerPrincipalFactory | 1 +
10 files changed, 632 insertions(+), 19 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/tck/mp-jwt-embedded/pom.xml
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/pom.xml b/tck/mp-jwt-embedded/pom.xml
index 361fd50..fac5766 100644
--- a/tck/mp-jwt-embedded/pom.xml
+++ b/tck/mp-jwt-embedded/pom.xml
@@ -86,6 +86,21 @@
<version>${version.arquillian}</version>
</dependency>
+ <dependency>
+ <groupId>org.bitbucket.b_c</groupId>
+ <artifactId>jose4j</artifactId>
+ <version>0.6.0</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.json.bind</groupId>
+ <artifactId>javax.json.bind-api</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish</groupId>
+ <artifactId>javax.json</artifactId>
+ <version>1.0.4</version>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/DefaultJWTCallerPrincipal.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/DefaultJWTCallerPrincipal.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/DefaultJWTCallerPrincipal.java
index d141a5f..2559b1e 100644
--- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/DefaultJWTCallerPrincipal.java
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/DefaultJWTCallerPrincipal.java
@@ -16,36 +16,319 @@
*/
package org.apache.tomee.microprofile.jwt;
+import org.eclipse.microprofile.jwt.Claims;
+import org.jose4j.jwt.JwtClaims;
+import org.jose4j.jwt.MalformedClaimException;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+import javax.security.auth.Subject;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
- * A default implementation of JWTCallerPrincipal that wraps Nimbus library used by default here
- * We could create another implementation using plain JSON-P
- *
+ * A default implementation of JWTCallerPrincipal using jose4j
+ * Another implementation could use nimbus and another plain JSON-P
*/
public class DefaultJWTCallerPrincipal extends JWTCallerPrincipal {
+ private static Logger logger = Logger.getLogger(DefaultJWTCallerPrincipal.class.getName());
+ private String jwt;
+ private String type;
+ private JwtClaims claimsSet;
+
/**
- * Create a JWTCallerPrincipal with the caller's name
+ * Create the DefaultJWTCallerPrincipal from the parsed JWT token and the extracted principal name
*
- * @param name - caller's name
+ * @param jwt - the parsed JWT token representation
+ * @param name - the extracted unqiue name to use as the principal name; from "upn", "preferred_username" or "sub" claim
*/
- public DefaultJWTCallerPrincipal(final String name) {
+ public DefaultJWTCallerPrincipal(String jwt, String type, JwtClaims claimsSet, String name) {
super(name);
+ this.jwt = jwt;
+ this.type = type;
+ this.claimsSet = claimsSet;
+ fixJoseTypes();
+ }
+
+ @Override
+ public Set<String> getAudience() {
+ Set<String> audSet = new HashSet<>();
+ try {
+ List<String> audList = claimsSet.getStringListClaimValue("aud");
+ if (audList != null) {
+ audSet.addAll(audList);
+ }
+ } catch (MalformedClaimException e) {
+ try {
+ String aud = claimsSet.getStringClaimValue("aud");
+ audSet.add(aud);
+ } catch (MalformedClaimException e1) {
+ }
+ }
+ return audSet;
}
@Override
- public String toString(final boolean showAll) {
- return null;
+ public Set<String> getGroups() {
+ HashSet<String> groups = new HashSet<>();
+ try {
+ List<String> globalGroups = claimsSet.getStringListClaimValue("groups");
+ if (globalGroups != null) {
+ groups.addAll(globalGroups);
+ }
+ } catch (MalformedClaimException e) {
+ e.printStackTrace();
+ }
+ return groups;
}
+
@Override
public Set<String> getClaimNames() {
- return null;
+ return new HashSet<>(claimsSet.getClaimNames());
+ }
+
+ @Override
+ public Object getClaim(String claimName) {
+ Claims claimType = Claims.UNKNOWN;
+ Object claim = null;
+ try {
+ claimType = Claims.valueOf(claimName);
+ } catch (IllegalArgumentException e) {
+ }
+ // Handle the jose4j NumericDate types and
+ switch (claimType) {
+ case exp:
+ case iat:
+ case auth_time:
+ case nbf:
+ case updated_at:
+ try {
+ claim = claimsSet.getClaimValue(claimType.name(), Long.class);
+ if (claim == null) {
+ claim = new Long(0);
+ }
+ } catch (MalformedClaimException e) {
+ }
+ break;
+ case groups:
+ claim = getGroups();
+ break;
+ case aud:
+ claim = getAudience();
+ break;
+ case UNKNOWN:
+ claim = claimsSet.getClaimValue(claimName);
+ break;
+ default:
+ claim = claimsSet.getClaimValue(claimType.name());
+ }
+ return claim;
}
@Override
- public <T> T getClaim(final String claimName) {
- return null;
+ public boolean implies(Subject subject) {
+ return false;
+ }
+
+ public String toString() {
+ return toString(false);
}
+
+ /**
+ * TODO: showAll is ignored and currently assumed true
+ *
+ * @param showAll - should all claims associated with the JWT be displayed or should only those defined in the
+ * JsonWebToken interface be displayed.
+ * @return JWTCallerPrincipal string view
+ */
+ @Override
+ public String toString(boolean showAll) {
+ String toString = "DefaultJWTCallerPrincipal{" +
+ "id='" + getTokenID() + '\'' +
+ ", name='" + getName() + '\'' +
+ ", expiration=" + getExpirationTime() +
+ ", notBefore=" + getClaim(Claims.nbf.name()) +
+ ", issuedAt=" + getIssuedAtTime() +
+ ", issuer='" + getIssuer() + '\'' +
+ ", audience=" + getAudience() +
+ ", subject='" + getSubject() + '\'' +
+ ", type='" + type + '\'' +
+ ", issuedFor='" + getClaim("azp") + '\'' +
+ ", authTime=" + getClaim("auth_time") +
+ ", givenName='" + getClaim("given_name") + '\'' +
+ ", familyName='" + getClaim("family_name") + '\'' +
+ ", middleName='" + getClaim("middle_name") + '\'' +
+ ", nickName='" + getClaim("nickname") + '\'' +
+ ", preferredUsername='" + getClaim("preferred_username") + '\'' +
+ ", email='" + getClaim("email") + '\'' +
+ ", emailVerified=" + getClaim(Claims.email_verified.name()) +
+ ", allowedOrigins=" + getClaim("allowedOrigins") +
+ ", updatedAt=" + getClaim("updated_at") +
+ ", acr='" + getClaim("acr") + '\'';
+ StringBuilder tmp = new StringBuilder(toString);
+ tmp.append(", groups=[");
+ for (String group : getGroups()) {
+ tmp.append(group);
+ tmp.append(',');
+ }
+ tmp.setLength(tmp.length() - 1);
+ tmp.append("]}");
+ return tmp.toString();
+ }
+
+ /**
+ * Convert the types jose4j uses for address, sub_jwk, and jwk
+ */
+ private void fixJoseTypes() {
+ if (claimsSet.hasClaim(Claims.address.name())) {
+ replaceMap(Claims.address.name());
+ }
+ if (claimsSet.hasClaim(Claims.jwk.name())) {
+ replaceMap(Claims.jwk.name());
+ }
+ if (claimsSet.hasClaim(Claims.sub_jwk.name())) {
+ replaceMap(Claims.sub_jwk.name());
+ }
+ // Handle custom claims
+ Set<String> customClaimNames = filterCustomClaimNames(claimsSet.getClaimNames());
+ for (String name : customClaimNames) {
+ Object claimValue = claimsSet.getClaimValue(name);
+ Class claimType = claimValue.getClass();
+ if (claimValue instanceof List) {
+ replaceList(name);
+ } else if (claimValue instanceof Map) {
+ replaceMap(name);
+ } else if (claimValue instanceof Number) {
+ replaceNumber(name);
+ }
+ }
+ }
+
+ /**
+ * Determine the custom claims in the set
+ *
+ * @param claimNames - the current set of claim names in this token
+ * @return the possibly empty set of names for non-Claims claims
+ */
+ private Set<String> filterCustomClaimNames(Collection<String> claimNames) {
+ HashSet<String> customNames = new HashSet<>(claimNames);
+ for (Claims claim : Claims.values()) {
+ customNames.remove(claim.name());
+ }
+ return customNames;
+ }
+
+ /**
+ * Replace the jose4j Map<String,Object> with a JsonObject
+ *
+ * @param name - claim name
+ */
+ private void replaceMap(String name) {
+ try {
+ Map<String, Object> map = claimsSet.getClaimValue(name, Map.class);
+ JsonObject jsonObject = replaceMap(map);
+ claimsSet.setClaim(name, jsonObject);
+ } catch (MalformedClaimException e) {
+ logger.log(Level.WARNING, "replaceMap failure for: " + name, e);
+ }
+ }
+
+ private 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();
+ }
+
+ private JsonValue wrapValue(Object value) {
+ JsonValue jsonValue = null;
+ 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 List) {
+ JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+ List list = (List) value;
+ for (Object element : list) {
+ if (element instanceof String) {
+ arrayBuilder.add(element.toString());
+ } else {
+ JsonValue jvalue = wrapValue(element);
+ arrayBuilder.add(jvalue);
+ }
+ }
+ jsonValue = arrayBuilder.build();
+ }
+ return jsonValue;
+ }
+
+
+ /**
+ * Replace the jose4j List<?> with a JsonArray
+ *
+ * @param name - claim name
+ */
+ private void replaceList(String name) {
+ try {
+ List list = claimsSet.getClaimValue(name, List.class);
+ JsonArray array = (JsonArray) wrapValue(list);
+ claimsSet.setClaim(name, array);
+ } catch (MalformedClaimException e) {
+ logger.log(Level.WARNING, "replaceList failure for: " + name, e);
+ }
+ }
+
+ private void replaceNumber(String name) {
+ try {
+ Number number = claimsSet.getClaimValue(name, Number.class);
+ JsonNumber jsonNumber = (JsonNumber) wrapValue(number);
+ claimsSet.setClaim(name, jsonNumber);
+ } catch (MalformedClaimException e) {
+ logger.log(Level.WARNING, "replaceNumber failure for: " + name, e);
+ }
+ }
+
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/DefaultJWTCallerPrincipalFactory.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/DefaultJWTCallerPrincipalFactory.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/DefaultJWTCallerPrincipalFactory.java
new file mode 100644
index 0000000..9c0a870
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/DefaultJWTCallerPrincipalFactory.java
@@ -0,0 +1,86 @@
+/*
+ * 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.Claims;
+import org.jose4j.jwa.AlgorithmConstraints;
+import org.jose4j.jws.AlgorithmIdentifiers;
+import org.jose4j.jwt.JwtClaims;
+import org.jose4j.jwt.MalformedClaimException;
+import org.jose4j.jwt.NumericDate;
+import org.jose4j.jwt.consumer.InvalidJwtException;
+import org.jose4j.jwt.consumer.JwtConsumer;
+import org.jose4j.jwt.consumer.JwtConsumerBuilder;
+import org.jose4j.jwt.consumer.JwtContext;
+
+/**
+ * A default implementation of the abstract JWTCallerPrincipalFactory that uses the Keycloak token parsing classes.
+ */
+public class DefaultJWTCallerPrincipalFactory extends JWTCallerPrincipalFactory {
+
+ /**
+ * Tries to load the JWTAuthContextInfo from CDI if the class level authContextInfo has not been set.
+ */
+ public DefaultJWTCallerPrincipalFactory() {
+ }
+
+ @Override
+ public JWTCallerPrincipal parse(final String token, final JWTAuthContextInfo authContextInfo) throws ParseException {
+ JWTCallerPrincipal principal = null;
+
+ try {
+ JwtConsumerBuilder builder = new JwtConsumerBuilder()
+ .setRequireExpirationTime()
+ .setRequireSubject()
+ .setSkipDefaultAudienceValidation()
+ .setExpectedIssuer(authContextInfo.getIssuedBy())
+ .setVerificationKey(authContextInfo.getSignerKey())
+ .setJwsAlgorithmConstraints(
+ new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.WHITELIST,
+ AlgorithmIdentifiers.RSA_USING_SHA256));
+ if (authContextInfo.getExpGracePeriodSecs() > 0) {
+ builder.setAllowedClockSkewInSeconds(authContextInfo.getExpGracePeriodSecs());
+ } else {
+ builder.setEvaluationTime(NumericDate.fromSeconds(0));
+ }
+
+ JwtConsumer jwtConsumer = builder.build();
+ JwtContext jwtContext = jwtConsumer.process(token);
+ String type = jwtContext.getJoseObjects().get(0).getHeader("typ");
+ // Validate the JWT and process it to the Claims
+ jwtConsumer.processContext(jwtContext);
+ JwtClaims claimsSet = jwtContext.getJwtClaims();
+
+ // We have to determine the unique name to use as the principal name. It comes from upn, preferred_username, sub in that order
+ String principalName = claimsSet.getClaimValue("upn", String.class);
+ if (principalName == null) {
+ principalName = claimsSet.getClaimValue("preferred_username", String.class);
+ if (principalName == null) {
+ principalName = claimsSet.getSubject();
+ }
+ }
+ claimsSet.setClaim(Claims.raw_token.name(), token);
+ principal = new DefaultJWTCallerPrincipal(token, type, claimsSet, principalName);
+ } catch (InvalidJwtException e) {
+ throw new ParseException("Failed to verify token", e);
+ } catch (MalformedClaimException e) {
+ throw new ParseException("Failed to verify token claims", e);
+ }
+
+ return principal;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTAuthContextInfo.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTAuthContextInfo.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTAuthContextInfo.java
new file mode 100644
index 0000000..a93e2cc
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTAuthContextInfo.java
@@ -0,0 +1,66 @@
+/*
+ * 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.security.interfaces.RSAPublicKey;
+
+/**
+ * The public key and expected issuer needed to validate a token.
+ */
+public class JWTAuthContextInfo {
+ private RSAPublicKey signerKey;
+ private String issuedBy;
+ private int expGracePeriodSecs = 60;
+
+ public JWTAuthContextInfo() {
+ }
+
+ public JWTAuthContextInfo(RSAPublicKey signerKey, String issuedBy) {
+ this.signerKey = signerKey;
+ this.issuedBy = issuedBy;
+ }
+
+ public JWTAuthContextInfo(JWTAuthContextInfo orig) {
+ this.signerKey = orig.signerKey;
+ this.issuedBy = orig.issuedBy;
+ this.expGracePeriodSecs = orig.expGracePeriodSecs;
+ }
+
+ public RSAPublicKey getSignerKey() {
+ return signerKey;
+ }
+
+ public void setSignerKey(RSAPublicKey signerKey) {
+ this.signerKey = signerKey;
+ }
+
+ public String getIssuedBy() {
+ return issuedBy;
+ }
+
+ public void setIssuedBy(String issuedBy) {
+ this.issuedBy = issuedBy;
+ }
+
+ public int getExpGracePeriodSecs() {
+ return expGracePeriodSecs;
+ }
+
+ public void setExpGracePeriodSecs(int expGracePeriodSecs) {
+ this.expGracePeriodSecs = expGracePeriodSecs;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipal.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipal.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipal.java
index 3eca47b..e130213 100644
--- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipal.java
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipal.java
@@ -14,18 +14,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.tomee.microprofile.jwt;
-import java.util.Optional;
import org.eclipse.microprofile.jwt.JsonWebToken;
+import java.util.Optional;
+
/**
* An abstract CallerPrincipal implementation that provides access to the JWT claims that are required by
* the microprofile token.
*/
public abstract class JWTCallerPrincipal implements JsonWebToken {
+
private String name;
/**
@@ -51,8 +52,8 @@ public abstract class JWTCallerPrincipal implements JsonWebToken {
*/
public abstract String toString(boolean showAll);
- public <T> Optional<T> claim(String claimName) {
- T claim = (T) getClaim(claimName);
+ public <T> Optional<T> claim(final String claimName) {
+ final T claim = (T) getClaim(claimName);
return Optional.ofNullable(claim);
}
}
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/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
new file mode 100644
index 0000000..a64f95a
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/JWTCallerPrincipalFactory.java
@@ -0,0 +1,124 @@
+/*
+ * 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.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ServiceLoader;
+
+/**
+ * The factory class that provides the token string to JWTCallerPrincipal parsing for a given implementation.
+ */
+public abstract class JWTCallerPrincipalFactory {
+ private static JWTCallerPrincipalFactory instance;
+
+ /**
+ * Obtain the JWTCallerPrincipalFactory that has been set or by using the ServiceLoader pattern.
+ *
+ * @return the factory instance
+ * @see #setInstance(JWTCallerPrincipalFactory)
+ */
+ public static JWTCallerPrincipalFactory instance() {
+ if (instance == null) {
+ synchronized (JWTCallerPrincipalFactory.class) {
+ if (instance != null) {
+ return instance;
+ }
+
+ ClassLoader cl = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+ @Override
+ public ClassLoader run() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ });
+ if (cl == null) {
+ cl = JWTCallerPrincipalFactory.class.getClassLoader();
+ }
+
+ JWTCallerPrincipalFactory newInstance = loadSpi(cl);
+
+ if (newInstance == null && cl != JWTCallerPrincipalFactory.class.getClassLoader()) {
+ cl = JWTCallerPrincipalFactory.class.getClassLoader();
+ newInstance = loadSpi(cl);
+ }
+ if (newInstance == null) {
+ throw new IllegalStateException("No JWTCallerPrincipalFactory implementation found!");
+ }
+
+ instance = newInstance;
+ }
+ }
+
+ return instance;
+ }
+
+ /**
+ * Look for a JWTCallerPrincipalFactory service implementation using the ServiceLoader.
+ *
+ * @param cl - the ClassLoader to pass into the {@link ServiceLoader#load(Class, ClassLoader)} method.
+ * @return the JWTCallerPrincipalFactory if found, null otherwise
+ */
+ private static JWTCallerPrincipalFactory loadSpi(ClassLoader cl) {
+ if (cl == null) {
+ return null;
+ }
+
+ // start from the root CL and go back down to the TCCL
+ JWTCallerPrincipalFactory instance = loadSpi(cl.getParent());
+
+ if (instance == null) {
+ ServiceLoader<JWTCallerPrincipalFactory> sl = ServiceLoader.load(JWTCallerPrincipalFactory.class, cl);
+ URL u = cl.getResource("/META-INF/services/org.eclipse.microprofile.jwt.principal.JWTCallerPrincipalFactory");
+ System.out.printf("JWTCallerPrincipalFactory, cl=%s, u=%s, sl=%s\n", cl, u, sl);
+ try {
+ for (JWTCallerPrincipalFactory spi : sl) {
+ if (instance != null) {
+ throw new IllegalStateException(
+ "Multiple JWTCallerPrincipalFactory implementations found: "
+ + spi.getClass().getName() + " and "
+ + instance.getClass().getName());
+ } else {
+ System.out.printf("sl=%s, loaded=%s\n", sl, spi);
+ instance = spi;
+ }
+ }
+ } catch (Throwable e) {
+ System.err.printf("Warning: %s\n", e.getMessage());
+ }
+ }
+ return instance;
+ }
+
+ /**
+ * Set the instance. It is used by OSGi environment where service loader pattern is not supported.
+ *
+ * @param resolver the instance to use.
+ */
+ public static void setInstance(JWTCallerPrincipalFactory resolver) {
+ instance = resolver;
+ }
+
+ /**
+ * Parse the given bearer token string into a JWTCallerPrincipal instance.
+ *
+ * @param token - the bearer token provided for authorization
+ * @return A JWTCallerPrincipal representation for the token.
+ * @throws ParseException on parse or verification failure.
+ */
+ public abstract JWTCallerPrincipal parse(String token, JWTAuthContextInfo authContextInfo) throws ParseException;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/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 2197745..bffccf2 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
@@ -16,10 +16,7 @@
*/
package org.apache.tomee.microprofile.jwt;
-import org.eclipse.microprofile.auth.LoginConfig;
-
import javax.enterprise.context.ApplicationScoped;
-import java.net.URI;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/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 cb235fa..092ad9d 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
@@ -40,6 +40,9 @@ public class MPJWTFilter implements Filter {
@Inject
private MPJWTContext context;
+ @Inject
+ private JWTAuthContextInfo authContextInfo;
+
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
// get configuration
@@ -60,7 +63,7 @@ public class MPJWTFilter implements Filter {
// todo get JWT and do validation
// todo not sure what to do with the realm
- final JsonWebToken jsonWebToken = new DefaultJWTCallerPrincipal("bla"); // will be build during validation
+ final JsonWebToken jsonWebToken = null; // will be build during validation
// now wrap the httpServletRequest and override the principal so CXF can propagate into the SecurityContext
chain.doFilter(new HttpServletRequestWrapper(httpServletRequest) {
@@ -89,4 +92,9 @@ public class MPJWTFilter implements Filter {
}
+ protected JsonWebToken validate(String bearerToken) throws ParseException {
+ JWTCallerPrincipalFactory factory = JWTCallerPrincipalFactory.instance();
+ return factory.parse(bearerToken, authContextInfo);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ParseException.java
----------------------------------------------------------------------
diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ParseException.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ParseException.java
new file mode 100644
index 0000000..60269b0
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ParseException.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;
+
+/**
+ * The exception thrown when
+ */
+public class ParseException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public ParseException(String message) {
+ super(message);
+ }
+
+ public ParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/c5b38b37/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
new file mode 100644
index 0000000..67f39db
--- /dev/null
+++ b/tck/mp-jwt-embedded/src/main/resources/META-INF/services/org.eclipse.microprofile.jwt.principal.JWTCallerPrincipalFactory
@@ -0,0 +1 @@
+org.apache.tomee.microprofile.jwt.DefaultJWTCallerPrincipalFactory
\ No newline at end of file