http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/DefaultJWTCallerPrincipalFactory.java ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/DefaultJWTCallerPrincipalFactory.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/DefaultJWTCallerPrincipalFactory.java new file mode 100644 index 0000000..feb2008 --- /dev/null +++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/DefaultJWTCallerPrincipalFactory.java @@ -0,0 +1,92 @@ +/* + * 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.principal; + +import org.apache.tomee.microprofile.jwt.ParseException; +import org.apache.tomee.microprofile.jwt.config.JWTAuthContextInfo; +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; + + try { + final 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)); + } + + final JwtConsumer jwtConsumer = builder.build(); + final JwtContext jwtContext = jwtConsumer.process(token); + final 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 (final InvalidJwtException e) { + throw new ParseException("Failed to verify token", e); + + } catch (final 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/72c08321/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/JWTCallerPrincipal.java ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/JWTCallerPrincipal.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/JWTCallerPrincipal.java new file mode 100644 index 0000000..d8e3c4c --- /dev/null +++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/JWTCallerPrincipal.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.principal; + + +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; + + /** + * Create a JWTCallerPrincipal with the caller's name + * + * @param name - caller's name + */ + public JWTCallerPrincipal(final String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + /** + * Generate a human readable version of the caller principal and associated JWT. + * + * @param showAll - should all claims associated with the JWT be displayed or should only those defined in the + * JsonWebToken interface be displayed. + * @return human readable presentation of the caller principal and associated JWT. + */ + public abstract String toString(final boolean showAll); + + 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/72c08321/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/JWTCallerPrincipalFactory.java ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/JWTCallerPrincipalFactory.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/JWTCallerPrincipalFactory.java new file mode 100644 index 0000000..e7ebcd6 --- /dev/null +++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/principal/JWTCallerPrincipalFactory.java @@ -0,0 +1,129 @@ +/* + * 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.principal; + +import org.apache.tomee.microprofile.jwt.ParseException; +import org.apache.tomee.microprofile.jwt.config.JWTAuthContextInfo; + +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.apache.tomee.microprofile.jwt.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 (final 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(final 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(final String token, final JWTAuthContextInfo authContextInfo) throws ParseException; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/mp-jwt/src/main/resources/META-INF/beans.xml ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/resources/META-INF/beans.xml b/mp-jwt/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..330c7f6 --- /dev/null +++ b/mp-jwt/src/main/resources/META-INF/beans.xml @@ -0,0 +1 @@ +<beans/> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/mp-jwt/src/main/resources/META-INF/org.apache.openejb.extension ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/resources/META-INF/org.apache.openejb.extension b/mp-jwt/src/main/resources/META-INF/org.apache.openejb.extension new file mode 100644 index 0000000..9734019 --- /dev/null +++ b/mp-jwt/src/main/resources/META-INF/org.apache.openejb.extension @@ -0,0 +1 @@ +org.apache.tomee.microprofile.jwt.jaxrs.MPJWPProviderRegistration \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/mp-jwt/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/mp-jwt/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 0000000..d5eea47 --- /dev/null +++ b/mp-jwt/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1 @@ +org.apache.tomee.microprofile.jwt.cdi.MPJWTCDIExtension \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/mp-jwt/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/mp-jwt/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer new file mode 100644 index 0000000..100e625 --- /dev/null +++ b/mp-jwt/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +org.apache.tomee.microprofile.jwt.MPJWTInitializer \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/mp-jwt/src/main/resources/META-INF/services/org.apache.tomee.microprofile.jwt.principal.JWTCallerPrincipalFactory ---------------------------------------------------------------------- diff --git a/mp-jwt/src/main/resources/META-INF/services/org.apache.tomee.microprofile.jwt.principal.JWTCallerPrincipalFactory b/mp-jwt/src/main/resources/META-INF/services/org.apache.tomee.microprofile.jwt.principal.JWTCallerPrincipalFactory new file mode 100644 index 0000000..21c9831 --- /dev/null +++ b/mp-jwt/src/main/resources/META-INF/services/org.apache.tomee.microprofile.jwt.principal.JWTCallerPrincipalFactory @@ -0,0 +1 @@ +org.apache.tomee.microprofile.jwt.principal.DefaultJWTCallerPrincipalFactory \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 45f8d5a..3be6c9f 100644 --- a/pom.xml +++ b/pom.xml @@ -508,6 +508,7 @@ <module>gradle</module> <module>container</module> <module>server</module> + <module>mp-jwt</module> <module>examples</module> <module>assembly</module> <module>tck</module> http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/pom.xml ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/pom.xml b/tck/mp-jwt-embedded/pom.xml index 2af8f28..702e56e 100644 --- a/tck/mp-jwt-embedded/pom.xml +++ b/tck/mp-jwt-embedded/pom.xml @@ -34,19 +34,8 @@ <dependencies> <dependency> <groupId>${project.groupId}</groupId> - <artifactId>javaee-api</artifactId> - </dependency> - <dependency> - <groupId>${project.groupId}</groupId> - <artifactId>openejb-core</artifactId> - <version>${project.version}</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>${project.groupId}</groupId> - <artifactId>openejb-cxf-rs</artifactId> - <version>${project.version}</version> - <scope>provided</scope> + <artifactId>mp-jwt</artifactId> + <version>${tomee.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> @@ -97,33 +86,6 @@ <version>${version.arquillian}</version> </dependency> - <dependency> - <groupId>org.bitbucket.b_c</groupId> - <artifactId>jose4j</artifactId> - <version>0.6.0</version> - </dependency> - - <dependency> - <groupId>org.apache.geronimo.specs</groupId> - <artifactId>geronimo-json_1.1_spec</artifactId> - <version>1.0</version> - </dependency> - <dependency> - <groupId>org.apache.geronimo.specs</groupId> - <artifactId>geronimo-jsonb_1.0_spec</artifactId> - <version>1.0</version> - </dependency> - <dependency> - <groupId>org.apache.johnzon</groupId> - <artifactId>johnzon-jsonb</artifactId> - <version>1.1.2</version> - </dependency> - <dependency> - <groupId>org.apache.tomcat</groupId> - <artifactId>tomcat-catalina</artifactId> - <version>${tomcat.version}</version> - <scope>provided</scope> - </dependency> </dependencies> <build> @@ -134,7 +96,7 @@ <version>3.6.1</version> <configuration> <source>1.8</source> - <target>1.8</target>JwSecTest + <target>1.8</target> </configuration> </plugin> <!-- http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/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 deleted file mode 100644 index 25bf828..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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.apache.tomee.microprofile.jwt.config.JWTAuthContextInfo; -import org.apache.tomee.microprofile.jwt.principal.JWTCallerPrincipalFactory; -import org.eclipse.microprofile.jwt.JsonWebToken; - -import javax.inject.Inject; -import javax.security.auth.Subject; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.annotation.WebFilter; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; -import java.io.IOException; -import java.security.Principal; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.function.Function; -import java.util.stream.Collectors; - -// async is supported because we only need to do work on the way in -@WebFilter(asyncSupported = true, urlPatterns = "/*") -public class MPJWTFilter implements Filter { - - @Inject - private JWTAuthContextInfo authContextInfo; - - @Override - public void init(final FilterConfig filterConfig) throws ServletException { - // nothing so far - - } - - @Override - public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { - - final HttpServletRequest httpServletRequest = (HttpServletRequest) request; - - // now wrap the httpServletRequest and override the principal so CXF can propagate into the SecurityContext - try { - chain.doFilter(new MPJWTServletRequestWrapper(httpServletRequest, authContextInfo), response); - - } catch (final Exception e) { - // this is an alternative to the @Provider bellow which requires registration on the fly - // or users to add it into their webapp for scanning or into the Application itself - if (MPJWTException.class.isInstance(e)) { - final MPJWTException jwtException = MPJWTException.class.cast(e); - HttpServletResponse.class.cast(response).sendError(jwtException.getStatus(), jwtException.getMessage()); - } - - if (MPJWTException.class.isInstance(e.getCause())) { - final MPJWTException jwtException = MPJWTException.class.cast(e.getCause()); - HttpServletResponse.class.cast(response).sendError(jwtException.getStatus(), jwtException.getMessage()); - } - - } - - } - - @Override - public void destroy() { - // nothing to do - } - - private static Function<HttpServletRequest, JsonWebToken> token(final HttpServletRequest httpServletRequest, final JWTAuthContextInfo authContextInfo) { - - return new Function<HttpServletRequest, JsonWebToken>() { - - private JsonWebToken jsonWebToken; - - @Override - public JsonWebToken apply(final HttpServletRequest request) { - - // not sure it's worth having synchronization inside a single request - // worth case, we would parse and validate the token twice - if (jsonWebToken != null) { - return jsonWebToken; - } - - final String authorizationHeader = httpServletRequest.getHeader("Authorization"); - if (authorizationHeader == null || authorizationHeader.isEmpty()) { - throw new MissingAuthorizationHeaderException(); - } - - if (!authorizationHeader.toLowerCase(Locale.ENGLISH).startsWith("bearer ")) { - throw new BadAuthorizationPrefixException(authorizationHeader); - } - - final String token = authorizationHeader.substring("bearer ".length()); - try { - jsonWebToken = validate(token, authContextInfo); - - } catch (final ParseException e) { - throw new InvalidTokenException(token, e); - } - - return jsonWebToken; - - } - }; - - } - - private static JsonWebToken validate(final String bearerToken, final JWTAuthContextInfo authContextInfo) throws ParseException { - JWTCallerPrincipalFactory factory = JWTCallerPrincipalFactory.instance(); - return factory.parse(bearerToken, authContextInfo); - } - - public static class MPJWTServletRequestWrapper extends HttpServletRequestWrapper { - - private final Function<HttpServletRequest, JsonWebToken> tokenFunction; - private final HttpServletRequest request; - - /** - * Constructs a request object wrapping the given request. - * - * @param request The request to wrap - * @param authContextInfo the context configuration to validate the token - * @throws IllegalArgumentException if the request is null - */ - public MPJWTServletRequestWrapper(final HttpServletRequest request, final JWTAuthContextInfo authContextInfo) { - super(request); - this.request = request; - tokenFunction = token(request, authContextInfo); - - // this is so that the MPJWTProducer can find the function and apply it if necessary - request.setAttribute(JsonWebToken.class.getName(), tokenFunction); - request.setAttribute("javax.security.auth.subject.callable", (Callable<Subject>) () -> { - final Set<Principal> principals = new LinkedHashSet<>(); - final JsonWebToken namePrincipal = tokenFunction.apply(request); - principals.add(namePrincipal); - principals.addAll(namePrincipal.getGroups().stream().map(role -> (Principal) () -> role).collect(Collectors.toList())); - return new Subject(true, principals, Collections.emptySet(), Collections.emptySet()); - }); - } - - @Override - public Principal getUserPrincipal() { - return tokenFunction.apply(request); - } - - @Override - public boolean isUserInRole(String role) { - final JsonWebToken jsonWebToken = tokenFunction.apply(request); - return jsonWebToken.getGroups().contains(role); - } - - @Override - public String getAuthType() { - return "MP-JWT"; - } - - } - - private static abstract class MPJWTException extends RuntimeException { - - public MPJWTException() { - super(); - } - - public MPJWTException(final Throwable cause) { - super(cause); - } - - public abstract int getStatus(); - - public abstract String getMessage(); - } - - private static class MissingAuthorizationHeaderException extends MPJWTException { - - @Override - public int getStatus() { - return HttpServletResponse.SC_UNAUTHORIZED; - } - - @Override - public String getMessage() { - return "No authorization header provided. Can't validate the JWT."; - } - } - - private static class BadAuthorizationPrefixException extends MPJWTException { - - private String authorizationHeader; - - public BadAuthorizationPrefixException(final String authorizationHeader) { - this.authorizationHeader = authorizationHeader; - } - - @Override - public int getStatus() { - return HttpServletResponse.SC_UNAUTHORIZED; - } - - @Override - public String getMessage() { - return "Authorization header does not use the Bearer prefix. Can't validate header " + authorizationHeader; - } - } - - private static class InvalidTokenException extends MPJWTException { - - private final String token; - - public InvalidTokenException(final String token, final Throwable cause) { - super(cause); - this.token = token; - } - - @Override - public int getStatus() { - return HttpServletResponse.SC_UNAUTHORIZED; - } - - @Override - public String getMessage() { - return "Invalid or not parsable JWT " + token; // we might want to break down the exceptions so we can have more messages. - } - } - - @Provider // would be the ideal but not automatically registered - public static class MPJWTExceptionMapper implements ExceptionMapper<MPJWTException> { - - @Override - public Response toResponse(final MPJWTException exception) { - return Response.status(exception.getStatus()).entity(exception.getMessage()).build(); - } - - } -} http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/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 deleted file mode 100644 index cede7dc..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTInitializer.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.auth.LoginConfig; - -import javax.servlet.FilterRegistration; -import javax.servlet.ServletContainerInitializer; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.annotation.HandlesTypes; -import javax.ws.rs.core.Application; -import java.util.Set; - -/** - * Responsible for adding the filter into the chain and doing all other initialization - */ -@HandlesTypes(LoginConfig.class) -public class MPJWTInitializer implements ServletContainerInitializer { - - @Override - public void onStartup(final Set<Class<?>> classes, final ServletContext ctx) throws ServletException { - - if (classes == null || classes.isEmpty()) { - return; // no classe having @LoginConfig on it - } - - for (Class<?> clazz : classes) { - final LoginConfig loginConfig = clazz.getAnnotation(LoginConfig.class); - - if (loginConfig.authMethod() == null && !"MP-JWT".equals(loginConfig.authMethod())) { - continue; - } - - if (!Application.class.isAssignableFrom(clazz)) { - continue; - // do we really want Application? - // See https://github.com/eclipse/microprofile-jwt-auth/issues/70 to clarify this point - } - - final FilterRegistration.Dynamic mpJwtFilter = ctx.addFilter("mp-jwt-filter", MPJWTFilter.class); - mpJwtFilter.setAsyncSupported(true); - mpJwtFilter.addMappingForUrlPatterns(null, false, "/*"); - - break; // no need to add it more than once - } - - } - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/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 deleted file mode 100644 index d9572d5..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/ParseException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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(final String message) { - super(message); - } - - public ParseException(final String message, final Throwable cause) { - super(message, cause); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimBean.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimBean.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimBean.java deleted file mode 100644 index 6c7a00d..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimBean.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * 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.cdi; - -import org.eclipse.microprofile.jwt.Claim; -import org.eclipse.microprofile.jwt.ClaimValue; -import org.eclipse.microprofile.jwt.Claims; -import org.eclipse.microprofile.jwt.JsonWebToken; - -import javax.enterprise.context.Dependent; -import javax.enterprise.context.RequestScoped; -import javax.enterprise.context.spi.CreationalContext; -import javax.enterprise.inject.Instance; -import javax.enterprise.inject.Vetoed; -import javax.enterprise.inject.spi.Annotated; -import javax.enterprise.inject.spi.Bean; -import javax.enterprise.inject.spi.BeanManager; -import javax.enterprise.inject.spi.InjectionPoint; -import javax.enterprise.inject.spi.PassivationCapable; -import javax.enterprise.util.AnnotationLiteral; -import javax.inject.Inject; -import javax.inject.Provider; -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObject; -import javax.json.JsonValue; -import javax.json.bind.Jsonb; -import java.lang.annotation.Annotation; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.logging.Logger; - -@Vetoed -public class ClaimBean<T> implements Bean<T>, PassivationCapable { - - private static final Logger logger = Logger.getLogger(MPJWTCDIExtension.class.getName()); - - private static final Set<Annotation> QUALIFIERS = new HashSet<>(); - - static { - QUALIFIERS.add(new ClaimLiteral()); - } - - @Inject - private Jsonb jsonb; - - private final BeanManager bm; - private final Class rawType; - private final Set<Type> types; - private final String id; - private final Class<? extends Annotation> scope; - - public ClaimBean(final BeanManager bm, final Type type) { - this.bm = bm; - types = new HashSet<>(); - types.add(type); - rawType = getRawType(type); - this.id = "ClaimBean_" + types; - scope = Dependent.class; - } - - private Class getRawType(final Type type) { - if (Class.class.isInstance(type)) { - return Class.class.cast(type); - - } else if (ParameterizedType.class.isInstance(type)) { - final ParameterizedType paramType = ParameterizedType.class.cast(type); - return Class.class.cast(paramType.getRawType()); - } - - throw new UnsupportedOperationException("Unsupported type " + type); - } - - - @Override - public Set<InjectionPoint> getInjectionPoints() { - return Collections.emptySet(); - } - - @Override - public Class<?> getBeanClass() { - return rawType; - } - - @Override - public boolean isNullable() { - return false; - } - - @Override - public void destroy(final T instance, final CreationalContext<T> context) { - logger.finest("Destroying CDI Bean for type " + types.iterator().next()); - } - - @Override - public Set<Type> getTypes() { - return types; - } - - @Override - public Set<Annotation> getQualifiers() { - return QUALIFIERS; - } - - @Override - public Class<? extends Annotation> getScope() { - return scope; - } - - @Override - public String getName() { - return null; - } - - @Override - public Set<Class<? extends Annotation>> getStereotypes() { - return Collections.emptySet(); - } - - @Override - public boolean isAlternative() { - return false; - } - - @Override - public String getId() { - return id; - } - - @Override - public T create(final CreationalContext<T> context) { - logger.finest("Creating CDI Bean for type " + types.iterator().next()); - final InjectionPoint ip = (InjectionPoint) bm.getInjectableReference(new ClaimInjectionPoint(this), context); - if (ip == null) { - throw new IllegalStateException("Could not retrieve InjectionPoint for type " + types.iterator().next()); - } - - final Annotated annotated = ip.getAnnotated(); - final Claim claim = annotated.getAnnotation(Claim.class); - final String key = getClaimKey(claim); - - logger.finest(String.format("Found Claim injection with name=%s and for %s", key, ip.toString())); - - if (ParameterizedType.class.isInstance(annotated.getBaseType())) { - final ParameterizedType paramType = ParameterizedType.class.cast(annotated.getBaseType()); - final Type rawType = paramType.getRawType(); - if (Class.class.isInstance(rawType) && paramType.getActualTypeArguments().length == 1) { - - final Class<?> rawTypeClass = ((Class<?>) rawType); - - // handle Provider<T> - if (rawTypeClass.isAssignableFrom(Provider.class)) { - final Type providerType = paramType.getActualTypeArguments()[0]; - if (ParameterizedType.class.isInstance(providerType) && isOptional(ParameterizedType.class.cast(providerType))) { - return (T) Optional.ofNullable(getClaimValue(key)); - } - return getClaimValue(key); - } - - // handle Instance<T> - if (rawTypeClass.isAssignableFrom(Instance.class)) { - final Type instanceType = paramType.getActualTypeArguments()[0]; - if (ParameterizedType.class.isInstance(instanceType) && isOptional(ParameterizedType.class.cast(instanceType))) { - return (T) Optional.ofNullable(getClaimValue(key)); - } - return getClaimValue(key); - } - - // handle ClaimValue<T> - if (rawTypeClass.isAssignableFrom(ClaimValue.class)) { - final Type claimValueType = paramType.getActualTypeArguments()[0]; - - final ClaimValueWrapper claimValueWrapper = new ClaimValueWrapper(key); - if (ParameterizedType.class.isInstance(claimValueType) && isOptional(ParameterizedType.class.cast(claimValueType))) { - claimValueWrapper.setValue(() -> { - final T claimValue = getClaimValue(key); - return Optional.ofNullable(claimValue); - }); - - } else if (ParameterizedType.class.isInstance(claimValueType) && isSet(ParameterizedType.class.cast(claimValueType))) { - claimValueWrapper.setValue(() -> { - final T claimValue = getClaimValue(key); - return claimValue; - }); - - } else if (ParameterizedType.class.isInstance(claimValueType) && isList(ParameterizedType.class.cast(claimValueType))) { - claimValueWrapper.setValue(() -> { - final T claimValue = getClaimValue(key); - return claimValue; - }); - - } else if (Class.class.isInstance(claimValueType)) { - claimValueWrapper.setValue(() -> { - final T claimValue = getClaimValue(key); - return claimValue; - }); - - } else { - throw new IllegalArgumentException("Unsupported ClaimValue type " + claimValueType.toString()); - } - - return (T) claimValueWrapper; - } - - // handle Optional<T> - if (rawTypeClass.isAssignableFrom(Optional.class)) { - return getClaimValue(key); - } - - // handle Set<T> - if (rawTypeClass.isAssignableFrom(Set.class)) { - return getClaimValue(key); - } - - // handle List<T> - if (rawTypeClass.isAssignableFrom(List.class)) { - return getClaimValue(key); - } - } - - } else if (annotated.getBaseType().getTypeName().startsWith("javax.json.Json")) { - // handle JsonValue<T> (number, string, etc) - return (T) toJson(key); - - } else { - // handle Raw types - return getClaimValue(key); - } - - throw new IllegalStateException("Unhandled Claim type " + annotated.getBaseType()); - } - - public static String getClaimKey(final Claim claim) { - return claim.standard() == Claims.UNKNOWN ? claim.value() : claim.standard().name(); - } - - private T getClaimValue(final String name) { - final Bean<?> bean = bm.resolve(bm.getBeans(JsonWebToken.class)); - JsonWebToken jsonWebToken = null; - if (RequestScoped.class.equals(bean.getScope())) { - jsonWebToken = JsonWebToken.class.cast(bm.getReference(bean, JsonWebToken.class, null)); - } - if (jsonWebToken == null || !bean.getScope().equals(RequestScoped.class)) { - logger.warning(String.format("Can't retrieve claim %s. No active principal.", name)); - return null; - } - - final Optional<T> claimValue = jsonWebToken.claim(name); - logger.finest(String.format("Found ClaimValue=%s for name=%s", claimValue, name)); - return claimValue.orElse(null); - } - - private JsonValue toJson(final String name) { - final T claimValue = getClaimValue(name); - return wrapValue(claimValue); - } - - private static final String TMP = "tmp"; - - private JsonValue wrapValue(final Object value) { - JsonValue jsonValue = null; - - if (JsonValue.class.isInstance(value)) { - // This may already be a JsonValue - jsonValue = JsonValue.class.cast(value); - - } else if (String.class.isInstance(value)) { - jsonValue = Json.createObjectBuilder() - .add(TMP, value.toString()) - .build() - .getJsonString(TMP); - - } else if (Number.class.isInstance(value)) { - final Number number = Number.class.cast(value); - if ((Long.class.isInstance(number)) || (Integer.class.isInstance(number))) { - jsonValue = Json.createObjectBuilder() - .add(TMP, number.longValue()) - .build() - .getJsonNumber(TMP); - - } else { - jsonValue = Json.createObjectBuilder() - .add(TMP, number.doubleValue()) - .build() - .getJsonNumber(TMP); - } - - } else if (Boolean.class.isInstance(value)) { - final Boolean flag = Boolean.class.cast(value); - jsonValue = flag ? JsonValue.TRUE : JsonValue.FALSE; - - } else if (Collection.class.isInstance(value)) { - final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); - final Collection list = Collection.class.cast(value); - - for (Object element : list) { - if (String.class.isInstance(element)) { - arrayBuilder.add(element.toString()); - - } else { - final JsonValue jvalue = wrapValue(element); - arrayBuilder.add(jvalue); - } - } - jsonValue = arrayBuilder.build(); - - } else if (Map.class.isInstance(value)) { - jsonValue = jsonb.fromJson(jsonb.toJson(value), JsonObject.class); - - } - return jsonValue; - } - - private boolean isOptional(final ParameterizedType type) { - return ((Class) type.getRawType()).isAssignableFrom(Optional.class); - } - - private boolean isSet(final ParameterizedType type) { - return ((Class) type.getRawType()).isAssignableFrom(Set.class); - } - - private boolean isList(final ParameterizedType type) { - return ((Class) type.getRawType()).isAssignableFrom(List.class); - } - - private static class ClaimLiteral extends AnnotationLiteral<Claim> implements Claim { - - @Override - public String value() { - return ""; - } - - @Override - public Claims standard() { - return Claims.UNKNOWN; - } - } - -} http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimInjectionPoint.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimInjectionPoint.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimInjectionPoint.java deleted file mode 100644 index 17be756..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimInjectionPoint.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.cdi; - -import javax.enterprise.inject.spi.Annotated; -import javax.enterprise.inject.spi.Bean; -import javax.enterprise.inject.spi.InjectionPoint; -import java.lang.annotation.Annotation; -import java.lang.reflect.Member; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.Set; - -public class ClaimInjectionPoint implements InjectionPoint { - - private final Bean bean; - - public ClaimInjectionPoint(final Bean bean) { - this.bean = bean; - } - - @Override - public boolean isTransient() { - return false; - } - - @Override - public boolean isDelegate() { - return false; - } - - @Override - public Type getType() { - return InjectionPoint.class; - } - - @Override - public Set<Annotation> getQualifiers() { - return Collections.singleton(DefaultLiteral.INSTANCE); - } - - @Override - public Member getMember() { - return null; - } - - @Override - public Bean<?> getBean() { - return bean; - } - - @Override - public Annotated getAnnotated() { - return null; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimValueWrapper.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimValueWrapper.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimValueWrapper.java deleted file mode 100644 index 2836abd..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/ClaimValueWrapper.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.cdi; - -import org.eclipse.microprofile.jwt.ClaimValue; - -import java.util.function.Supplier; - -public class ClaimValueWrapper<T> implements ClaimValue<T> { - - private final String name; - private Supplier<T> value; - - public ClaimValueWrapper(final String name) { - this.name = name; - } - - @Override - public String getName() { - return name; - } - - @Override - public T getValue() { - return value.get(); - } - - void setValue(final Supplier<T> value) { - this.value = value; - } - - @Override - public String toString() { - return "ClaimValueWrapper{" + - "name='" + name + '\'' + - ", value=" + value.get() + - '}'; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/DefaultLiteral.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/DefaultLiteral.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/DefaultLiteral.java deleted file mode 100644 index 273ff96..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/DefaultLiteral.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.cdi; - -import javax.enterprise.inject.Default; -import javax.enterprise.util.AnnotationLiteral; - -public class DefaultLiteral extends AnnotationLiteral<Default> implements Default { - public static final Default INSTANCE = new DefaultLiteral(); -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/JsonbProducer.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/JsonbProducer.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/JsonbProducer.java deleted file mode 100644 index 59f42c5..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/JsonbProducer.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.cdi; - -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.inject.Disposes; -import javax.enterprise.inject.Produces; -import javax.json.bind.Jsonb; -import javax.json.bind.JsonbBuilder; -import java.util.logging.Level; -import java.util.logging.Logger; - -@ApplicationScoped -// todo add a qualifier here so we isolate our instance from what applications would do -public class JsonbProducer { - - private static final Logger log = Logger.getLogger(MPJWTCDIExtension.class.getName()); - - @Produces - public Jsonb create() { - return JsonbBuilder.create(); - } - - public void close(@Disposes final Jsonb jsonb) { - try { - jsonb.close(); - - } catch (final Exception e) { - log.log(Level.WARNING, e.getMessage(), e); - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTCDIExtension.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTCDIExtension.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTCDIExtension.java deleted file mode 100644 index d1019ee..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTCDIExtension.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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.cdi; - -import org.apache.tomee.microprofile.jwt.MPJWTFilter; -import org.apache.tomee.microprofile.jwt.MPJWTInitializer; -import org.apache.tomee.microprofile.jwt.TCKTokenParser; -import org.apache.tomee.microprofile.jwt.config.JWTAuthContextInfoProvider; -import org.eclipse.microprofile.jwt.Claim; - -import javax.enterprise.event.Observes; -import javax.enterprise.inject.Instance; -import javax.enterprise.inject.spi.AfterBeanDiscovery; -import javax.enterprise.inject.spi.BeanManager; -import javax.enterprise.inject.spi.BeforeBeanDiscovery; -import javax.enterprise.inject.spi.Extension; -import javax.enterprise.inject.spi.InjectionPoint; -import javax.enterprise.inject.spi.ProcessInjectionPoint; -import javax.inject.Provider; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public class MPJWTCDIExtension implements Extension { - - private static final Predicate<InjectionPoint> NOT_PROVIDERS = ip -> (Class.class.isInstance(ip.getType())) || (ParameterizedType.class.isInstance(ip.getType()) && ((ParameterizedType) ip.getType()).getRawType() != Provider.class); - private static final Predicate<InjectionPoint> NOT_INSTANCES = ip -> (Class.class.isInstance(ip.getType())) || (ParameterizedType.class.isInstance(ip.getType()) && ((ParameterizedType) ip.getType()).getRawType() != Instance.class); - private static final Map<Type, Type> REPLACED_TYPES = new HashMap<>(); - - static { - REPLACED_TYPES.put(double.class, Double.class); - REPLACED_TYPES.put(int.class, Integer.class); - REPLACED_TYPES.put(float.class, Float.class); - REPLACED_TYPES.put(long.class, Long.class); - REPLACED_TYPES.put(boolean.class, Boolean.class); - } - - private Set<InjectionPoint> injectionPoints = new HashSet<>(); - - public void collectConfigProducer(@Observes final ProcessInjectionPoint<?, ?> pip) { - final Claim claim = pip.getInjectionPoint().getAnnotated().getAnnotation(Claim.class); - if (claim != null) { - injectionPoints.add(pip.getInjectionPoint()); - } - } - - public void registerClaimProducer(@Observes final AfterBeanDiscovery abd, final BeanManager bm) { - final Set<Type> types = injectionPoints.stream() - .filter(NOT_PROVIDERS) - .filter(NOT_INSTANCES) - .map(ip -> REPLACED_TYPES.getOrDefault(ip.getType(), ip.getType())) - .collect(Collectors.toSet()); - - final Set<Type> providerTypes = injectionPoints.stream() - .filter(NOT_PROVIDERS.negate()) - .map(ip -> ((ParameterizedType) ip.getType()).getActualTypeArguments()[0]) - .collect(Collectors.toSet()); - - final Set<Type> instanceTypes = injectionPoints.stream() - .filter(NOT_INSTANCES.negate()) - .map(ip -> ((ParameterizedType) ip.getType()).getActualTypeArguments()[0]) - .collect(Collectors.toSet()); - - types.addAll(providerTypes); - types.addAll(instanceTypes); - - types.stream() - .map(type -> new ClaimBean<>(bm, type)) - .forEach(abd::addBean); - } - - public void observeBeforeBeanDiscovery(@Observes final BeforeBeanDiscovery bbd, final BeanManager beanManager) { - bbd.addAnnotatedType(beanManager.createAnnotatedType(TCKTokenParser.class)); - bbd.addAnnotatedType(beanManager.createAnnotatedType(JsonbProducer.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)); - } - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTProducer.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTProducer.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTProducer.java deleted file mode 100644 index 42034b9..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/cdi/MPJWTProducer.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.cdi; - -import org.eclipse.microprofile.jwt.JsonWebToken; - -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.context.RequestScoped; -import javax.enterprise.inject.Produces; -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import java.util.Objects; -import java.util.function.Function; - -@ApplicationScoped -public class MPJWTProducer { - - @Inject - private HttpServletRequest httpServletRequest; - - @Produces - @RequestScoped - public JsonWebToken currentPrincipal() { - Objects.requireNonNull(httpServletRequest, "HTTP Servlet Request is required to produce a JSonWebToken principal."); - - // not very beautiful, but avoids having the MPJWTFilter setting the request or the principal in a thread local - // CDI integration already has one - dunno which approach is the best for now - final Object tokenAttribute = httpServletRequest.getAttribute(JsonWebToken.class.getName()); - if (Function.class.isInstance(tokenAttribute)) { - return (JsonWebToken) Function.class.cast(tokenAttribute).apply(httpServletRequest); - } - - return null; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthContextInfo.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthContextInfo.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthContextInfo.java deleted file mode 100644 index a969515..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthContextInfo.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.tomee.microprofile.jwt.config; - -import 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(final RSAPublicKey signerKey, final String issuedBy) { - this.signerKey = signerKey; - this.issuedBy = issuedBy; - } - - public JWTAuthContextInfo(final JWTAuthContextInfo orig) { - this.signerKey = orig.signerKey; - this.issuedBy = orig.issuedBy; - this.expGracePeriodSecs = orig.expGracePeriodSecs; - } - - public RSAPublicKey getSignerKey() { - return signerKey; - } - - public void setSignerKey(final RSAPublicKey signerKey) { - this.signerKey = signerKey; - } - - public String getIssuedBy() { - return issuedBy; - } - - public void setIssuedBy(final String issuedBy) { - this.issuedBy = issuedBy; - } - - public int getExpGracePeriodSecs() { - return expGracePeriodSecs; - } - - public void setExpGracePeriodSecs(final int expGracePeriodSecs) { - this.expGracePeriodSecs = expGracePeriodSecs; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthContextInfoProvider.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthContextInfoProvider.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthContextInfoProvider.java deleted file mode 100644 index 9247e04..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthContextInfoProvider.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.tomee.microprofile.jwt.config; - -import javax.enterprise.context.Dependent; -import javax.enterprise.inject.Produces; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; -import java.util.Base64; -import java.util.Optional; - -@Dependent -public class JWTAuthContextInfoProvider { - - @Produces - Optional<JWTAuthContextInfo> getOptionalContextInfo() throws NoSuchAlgorithmException, InvalidKeySpecException { - JWTAuthContextInfo contextInfo = new JWTAuthContextInfo(); - - // todo use MP Config to load the configuration - contextInfo.setIssuedBy("https://server.example.com"); - - final String pemEncoded = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEq" + - "Fyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwR" + - "TYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5e" + - "UF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9" + - "AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYn" + - "sIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9x" + - "nQIDAQAB"; - byte[] encodedBytes = Base64.getDecoder().decode(pemEncoded); - - final X509EncodedKeySpec spec = new X509EncodedKeySpec(encodedBytes); - final KeyFactory kf = KeyFactory.getInstance("RSA"); - final RSAPublicKey pk = (RSAPublicKey) kf.generatePublic(spec); - - contextInfo.setSignerKey(pk); - - return Optional.of(contextInfo); - } - - @Produces - JWTAuthContextInfo getContextInfo() throws InvalidKeySpecException, NoSuchAlgorithmException { - return getOptionalContextInfo().get(); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWPProviderRegistration.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWPProviderRegistration.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWPProviderRegistration.java deleted file mode 100644 index 34f152f..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWPProviderRegistration.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.jaxrs; - -import org.apache.openejb.observer.Observes; -import org.apache.openejb.server.cxf.rs.event.ExtensionProviderRegistration; -import org.apache.tomee.microprofile.jwt.MPJWTFilter; - -/** - * OpenEJB/TomEE hack to register a new provider on the fly - * Could be package in tomee only or done in another way - * - * As soon as Roberto is done with the packaging, we can remove all this and providers are going to be scanned automatically - */ -public class MPJWPProviderRegistration { - - public void registerProvider(@Observes final ExtensionProviderRegistration event) { - event.getProviders().add(new MPJWTFilter.MPJWTExceptionMapper()); - event.getProviders().add(new MPJWTSecurityAnnotationsInterceptorsFeature()); - } - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWTSecurityAnnotationsInterceptor.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWTSecurityAnnotationsInterceptor.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWTSecurityAnnotationsInterceptor.java deleted file mode 100644 index f604e6b..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWTSecurityAnnotationsInterceptor.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.apache.tomee.microprofile.jwt.jaxrs; - -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.io.IOException; -import java.lang.reflect.Method; -import java.net.HttpURLConnection; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; - -public class MPJWTSecurityAnnotationsInterceptor implements ContainerRequestFilter { - - private final javax.ws.rs.container.ResourceInfo resourceInfo; - private final ConcurrentMap<Method, Set<String>> rolesAllowed; - private final Set<Method> denyAll; - private final Set<Method> permitAll; - - public MPJWTSecurityAnnotationsInterceptor(final javax.ws.rs.container.ResourceInfo resourceInfo, - final ConcurrentMap<Method, Set<String>> rolesAllowed, - final Set<Method> denyAll, - final Set<Method> permitAll) { - this.resourceInfo = resourceInfo; - this.rolesAllowed = rolesAllowed; - this.denyAll = denyAll; - this.permitAll = permitAll; - } - - @Override - public void filter(final ContainerRequestContext requestContext) throws IOException { - if (permitAll.contains(resourceInfo.getResourceMethod())) { - return; - } - - if (denyAll.contains(resourceInfo.getResourceMethod())) { - forbidden(requestContext); - return; - } - - final Set<String> roles = rolesAllowed.get(resourceInfo.getResourceMethod()); - if (roles != null && !roles.isEmpty()) { - final SecurityContext securityContext = requestContext.getSecurityContext(); - for (String role : roles) { - if (!securityContext.isUserInRole(role)) { - forbidden(requestContext); - break; - } - } - } - - } - - private void forbidden(final ContainerRequestContext requestContext) { - requestContext.abortWith(Response.status(HttpURLConnection.HTTP_FORBIDDEN).build()); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tomee/blob/72c08321/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWTSecurityAnnotationsInterceptorsFeature.java ---------------------------------------------------------------------- diff --git a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWTSecurityAnnotationsInterceptorsFeature.java b/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWTSecurityAnnotationsInterceptorsFeature.java deleted file mode 100644 index 58b3203..0000000 --- a/tck/mp-jwt-embedded/src/main/java/org/apache/tomee/microprofile/jwt/jaxrs/MPJWTSecurityAnnotationsInterceptorsFeature.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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.jaxrs; - -import javax.annotation.security.DenyAll; -import javax.annotation.security.PermitAll; -import javax.annotation.security.RolesAllowed; -import javax.ws.rs.container.DynamicFeature; -import javax.ws.rs.container.ResourceInfo; -import javax.ws.rs.core.FeatureContext; -import javax.ws.rs.ext.Provider; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -@Provider -public class MPJWTSecurityAnnotationsInterceptorsFeature implements DynamicFeature { - - private final ConcurrentMap<Method, Set<String>> rolesAllowed = new ConcurrentHashMap<>(); - private final Set<Method> denyAll = new HashSet<>(); - private final Set<Method> permitAll = new HashSet<>(); - - @Override - public void configure(final ResourceInfo resourceInfo, final FeatureContext context) { - - final boolean hasSecurity = processSecurityAnnotations(resourceInfo.getResourceClass(), resourceInfo.getResourceMethod()); - - if (hasSecurity) { // no need to add interceptor on the resources that don(t have any security requirements to enforce - context.register(new MPJWTSecurityAnnotationsInterceptor(resourceInfo, rolesAllowed, denyAll, permitAll)); - } - - } - - private boolean processSecurityAnnotations(final Class clazz, final Method method) { - - final List<Class<? extends Annotation>[]> classSecurityAnnotations = hasClassLevelAnnotations(clazz, - RolesAllowed.class, PermitAll.class, DenyAll.class); - - final List<Class<? extends Annotation>[]> methodSecurityAnnotations = hasMethodLevelAnnotations(method, - RolesAllowed.class, PermitAll.class, DenyAll.class); - - if (classSecurityAnnotations.size() == 0 && methodSecurityAnnotations.size() == 0) { - return false; // nothing to do - } - - /* - * Process annotations at the class level - */ - if (classSecurityAnnotations.size() > 1) { - throw new IllegalStateException(clazz.getName() + " has more than one security annotation (RolesAllowed, PermitAll, DenyAll)."); - } - - if (methodSecurityAnnotations.size() > 1) { - throw new IllegalStateException(method.toString() + " has more than one security annotation (RolesAllowed, PermitAll, DenyAll)."); - } - - if (methodSecurityAnnotations.size() == 0) { // no need to deal with class level annotations if the method has some - final RolesAllowed classRolesAllowed = (RolesAllowed) clazz.getAnnotation(RolesAllowed.class); - final PermitAll classPermitAll = (PermitAll) clazz.getAnnotation(PermitAll.class); - final DenyAll classDenyAll = (DenyAll) clazz.getAnnotation(DenyAll.class); - - if (classRolesAllowed != null) { - Set<String> roles = new HashSet<String>(); - final Set<String> previous = rolesAllowed.putIfAbsent(method, roles); - if (previous != null) { - roles = previous; - } - roles.addAll(Arrays.asList(classRolesAllowed.value())); - } - - if (classPermitAll != null) { - permitAll.add(method); - } - - if (classDenyAll != null) { - denyAll.add(method); - } - } - - final RolesAllowed mthdRolesAllowed = method.getAnnotation(RolesAllowed.class); - final PermitAll mthdPermitAll = method.getAnnotation(PermitAll.class); - final DenyAll mthdDenyAll = method.getAnnotation(DenyAll.class); - - if (mthdRolesAllowed != null) { - Set<String> roles = new HashSet<String>(); - final Set<String> previous = rolesAllowed.putIfAbsent(method, roles); - if (previous != null) { - roles = previous; - } - roles.addAll(Arrays.asList(mthdRolesAllowed.value())); - } - - if (mthdPermitAll != null) { - permitAll.add(method); - } - - if (mthdDenyAll != null) { - denyAll.add(method); - } - - return true; - } - - private List<Class<? extends Annotation>[]> hasClassLevelAnnotations(final Class clazz, final Class<? extends Annotation>... annotationsToCheck) { - final List<Class<? extends Annotation>[]> list = new ArrayList<>(); - for (Class<? extends Annotation> annotationToCheck : annotationsToCheck) { - if (clazz.isAnnotationPresent(annotationToCheck)) { - list.add(annotationsToCheck); - } - } - return list; - } - - private List<Class<? extends Annotation>[]> hasMethodLevelAnnotations(final Method method, final Class<? extends Annotation>... annotationsToCheck) { - final List<Class<? extends Annotation>[]> list = new ArrayList<>(); - for (Class<? extends Annotation> annotationToCheck : annotationsToCheck) { - if (method.isAnnotationPresent(annotationToCheck)) { - list.add(annotationsToCheck); - } - } - return list; - } - -} \ No newline at end of file