Skip to content
Permalink
Browse files
GERONIMO-6645 adding TokenAccessor to ensure testing a service layer …
…is very doable without mocking a request
  • Loading branch information
rmannibucau committed Aug 28, 2018
1 parent 23a8e4b commit d97df86e0a8a8f7662bcfdfa76d99191b7c3f762
Showing 5 changed files with 50 additions and 15 deletions.
@@ -63,14 +63,15 @@

import org.apache.geronimo.microprofile.impl.jwtauth.config.GeronimoJwtAuthConfig;
import org.apache.geronimo.microprofile.impl.jwtauth.jwt.ContextualJsonWebToken;
import org.apache.geronimo.microprofile.impl.jwtauth.servlet.TokenAccessor;
import org.apache.geronimo.microprofile.impl.jwtauth.servlet.JwtRequest;
import org.eclipse.microprofile.jwt.Claim;
import org.eclipse.microprofile.jwt.ClaimValue;
import org.eclipse.microprofile.jwt.Claims;
import org.eclipse.microprofile.jwt.JsonWebToken;

public class GeronimoJwtAuthExtension implements Extension {
private final ThreadLocal<JwtRequest> request = new ThreadLocal<>();
private final ThreadLocal<TokenAccessor> request = new ThreadLocal<>();

private final Collection<Injection> injectionPoints = new HashSet<>(8);
private final Collection<Throwable> errors = new ArrayList<>();
@@ -106,7 +107,7 @@ void addClaimBeans(@Observes final AfterBeanDiscovery afterBeanDiscovery) {
.qualifiers(Default.Literal.INSTANCE, Any.Literal.INSTANCE)
.scope(ApplicationScoped.class)
.createWith(ctx -> new ContextualJsonWebToken(() -> {
final JwtRequest request = this.request.get();
final TokenAccessor request = this.request.get();
if (request == null) {
throw new IllegalStateException("No JWT in this request");
}
@@ -143,7 +144,7 @@ private Optional<Injection> createInjection(final Claim claim, final Type type)
return createInjection(claim, arg)
.map(it -> new Injection(claim.value(), claim.standard(), type) {
@Override
Object createInstance(final JwtRequest jwtRequest) {
Object createInstance(final TokenAccessor jwtRequest) {
return ofNullable(it.createInstance(jwtRequest));
}
});
@@ -153,7 +154,7 @@ Object createInstance(final JwtRequest jwtRequest) {
return createInjection(claim, arg)
.map(it -> new Injection(claim.value(), claim.standard(), type) {
@Override
Object createInstance(final JwtRequest jwtRequest) {
Object createInstance(final TokenAccessor jwtRequest) {
return new ClaimValue<Object>() {
@Override
public String getName() {
@@ -178,7 +179,7 @@ public Object getValue() {
if (JsonString.class.isAssignableFrom(clazz)) {
return of(new Injection(claim.value(), claim.standard(), clazz) {
@Override
Object createInstance(final JwtRequest jwtRequest) {
Object createInstance(final TokenAccessor jwtRequest) {
final Object instance = super.createInstance(jwtRequest);
if (JsonString.class.isInstance(instance)) {
return instance;
@@ -190,7 +191,7 @@ Object createInstance(final JwtRequest jwtRequest) {
if (JsonNumber.class.isAssignableFrom(clazz)) {
return of(new Injection(claim.value(), claim.standard(), clazz) {
@Override
Object createInstance(final JwtRequest jwtRequest) {
Object createInstance(final TokenAccessor jwtRequest) {
final Object instance = super.createInstance(jwtRequest);
if (JsonNumber.class.isInstance(instance)) {
return instance;
@@ -205,7 +206,7 @@ Object createInstance(final JwtRequest jwtRequest) {
if (JsonArray.class.isAssignableFrom(clazz)) {
return of(new Injection(claim.value(), claim.standard(), clazz) {
@Override
Object createInstance(final JwtRequest jwtRequest) {
Object createInstance(final TokenAccessor jwtRequest) {
final Object instance = super.createInstance(jwtRequest);
if (instance == null) {
return null;
@@ -260,7 +261,7 @@ private static String getClaimName(final String name, final Claims val) {

public void execute(final HttpServletRequest req, final ServletRunnable task) {
try {
final JwtRequest jwtRequest = requireNonNull(JwtRequest.class.isInstance(req) ?
final TokenAccessor jwtRequest = requireNonNull(JwtRequest.class.isInstance(req) ?
JwtRequest.class.cast(req) : JwtRequest.class.cast(req.getAttribute(JwtRequest.class.getName())),
"No JwtRequest");
execute(jwtRequest, task);
@@ -269,7 +270,7 @@ public void execute(final HttpServletRequest req, final ServletRunnable task) {
}
}

public void execute(final JwtRequest req, final ServletRunnable task) throws ServletException, IOException {
public void execute(final TokenAccessor req, final ServletRunnable task) throws ServletException, IOException {
request.set(req); // we want to track it ourself to support propagation properly when needed
try {
task.run();
@@ -348,7 +349,7 @@ private Annotation literal() {
return new ClaimLiteral(name, claims);
}

Object createInstance(final JwtRequest jwtRequest) {
Object createInstance(final TokenAccessor jwtRequest) {
return transformer.apply(jwtRequest.getToken().getClaim(runtimeName));
}

@@ -31,6 +31,7 @@
import javax.json.JsonString;

import org.apache.geronimo.microprofile.impl.jwtauth.JwtException;
import org.apache.geronimo.microprofile.impl.jwtauth.cdi.GeronimoJwtAuthExtension;
import org.apache.geronimo.microprofile.impl.jwtauth.config.GeronimoJwtAuthConfig;
import org.eclipse.microprofile.jwt.Claims;
import org.eclipse.microprofile.jwt.JsonWebToken;
@@ -49,6 +50,9 @@ public class JwtParser {
@Inject
private SignatureValidator signatureValidator;

@Inject
private GeronimoJwtAuthExtension extension;

private JsonReaderFactory readerFactory;

private String defaultKid;
@@ -91,6 +95,10 @@ public JsonWebToken parse(final String jwt) {
}
signatureValidator.verifySignature(alg, kidMapper.loadKey(kid), jwt.substring(0, secondDot), jwt.substring(secondDot + 1));

return createToken(jwt, payload);
}

public GeronimoJsonWebToken createToken(final String jwt, final JsonObject payload) {
return new GeronimoJsonWebToken(jwt, payload);
}

@@ -80,7 +80,7 @@ public void doFilter(final ServletRequest request, final ServletResponse respons

try {
final JwtRequest req = new JwtRequest(service, headerName, prefix, httpServletRequest);
extension.execute(req, () -> chain.doFilter(req, response));
extension.execute(req.asTokenAccessor(), () -> chain.doFilter(req, response));
} catch (final Exception e) { // when not used with JAX-RS but directly Servlet
final HttpServletResponse httpServletResponse = HttpServletResponse.class.cast(response);
if (!httpServletResponse.isCommitted()) {
@@ -35,14 +35,12 @@
import org.apache.geronimo.microprofile.impl.jwtauth.jwt.JwtParser;
import org.eclipse.microprofile.jwt.JsonWebToken;

public class JwtRequest extends HttpServletRequestWrapper {
private final HttpServletRequest delegate;
public class JwtRequest extends HttpServletRequestWrapper implements TokenAccessor {
private final Supplier<JsonWebToken> tokenExtractor;
private volatile JsonWebToken token; // cache for perf reasons

public JwtRequest(final JwtParser service, final String header, final String prefix, final HttpServletRequest request) {
super(request);
this.delegate = request;

this.tokenExtractor = () -> {
if (token != null) {
@@ -54,7 +52,7 @@ public JwtRequest(final JwtParser service, final String header, final String pre
return token;
}

final String auth = delegate.getHeader(header);
final String auth = getHeader(header);
if (auth == null || auth.isEmpty()) {
throw new JwtException("No " + header + " header", HttpServletResponse.SC_UNAUTHORIZED);
}
@@ -81,6 +79,11 @@ public JwtRequest(final JwtParser service, final String header, final String pre
});
}

public TokenAccessor asTokenAccessor() {
return this;
}

@Override
public JsonWebToken getToken() {
return tokenExtractor.get();
}
@@ -0,0 +1,23 @@
/*
* 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.geronimo.microprofile.impl.jwtauth.servlet;

import org.eclipse.microprofile.jwt.JsonWebToken;

public interface TokenAccessor {
JsonWebToken getToken();
}

0 comments on commit d97df86

Please sign in to comment.