This is an automated email from the ASF dual-hosted git repository.

pzampino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 2eb3364  KNOX-2556 - Enhance JWTProvider to accept knox.id as Passcode 
Token (#424)
2eb3364 is described below

commit 2eb336426eddae345060bf0b697f6971c685c497
Author: Phil Zampino <[email protected]>
AuthorDate: Thu Mar 25 23:41:06 2021 -0400

    KNOX-2556 - Enhance JWTProvider to accept knox.id as Passcode Token (#424)
---
 gateway-provider-security-hadoopauth/pom.xml       |   5 +
 .../hadoopauth/filter/HadoopAuthPostFilter.java    |  10 +-
 .../provider/federation/jwt/JWTMessages.java       |   3 +
 .../federation/jwt/filter/AbstractJWTFilter.java   |  90 +++-
 .../federation/jwt/filter/JWTFederationFilter.java |  77 ++--
 .../provider/federation/AbstractJWTFilterTest.java | 479 +++++----------------
 .../provider/federation/CommonJWTFilterTest.java   |   6 +
 .../JWTAsHTTPBasicCredsFederationFilterTest.java   |  94 ++--
 .../federation/JWTFederationFilterTest.java        |  43 +-
 .../federation/TestJWTFederationFilter.java        |  56 +++
 ...okenIDAsHTTPBasicCredsFederationFilterTest.java | 419 ++++++++++++++++++
 11 files changed, 792 insertions(+), 490 deletions(-)

diff --git a/gateway-provider-security-hadoopauth/pom.xml 
b/gateway-provider-security-hadoopauth/pom.xml
index 4f2c775..796efcb 100755
--- a/gateway-provider-security-hadoopauth/pom.xml
+++ b/gateway-provider-security-hadoopauth/pom.xml
@@ -66,6 +66,11 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.knox</groupId>
             <artifactId>gateway-test-utils</artifactId>
             <scope>test</scope>
diff --git 
a/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthPostFilter.java
 
b/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthPostFilter.java
index ee32755..e2a5eb0 100755
--- 
a/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthPostFilter.java
+++ 
b/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthPostFilter.java
@@ -36,6 +36,7 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.knox.gateway.security.PrimaryPrincipal;
 import org.apache.knox.gateway.audit.api.AuditService;
 import org.apache.knox.gateway.audit.api.AuditServiceFactory;
@@ -78,7 +79,14 @@ public class HadoopAuthPostFilter implements Filter {
     Subject subject = null;
     if (shouldUseJwtFilter(jwtFilter, (HttpServletRequest) request)) {
       try {
-        subject = 
jwtFilter.createSubjectFromToken(jwtFilter.getWireToken(request));
+        Pair<JWTFederationFilter.TokenType, String> wireToken = 
jwtFilter.getWireToken(request);
+        JWTFederationFilter.TokenType tokenType = wireToken.getLeft();
+        String token = wireToken.getRight();
+        if (JWTFederationFilter.TokenType.JWT.equals(tokenType)) {
+          subject = jwtFilter.createSubjectFromToken(token);
+        } else if (JWTFederationFilter.TokenType.Passcode.equals(tokenType)) {
+          subject = jwtFilter.createSubjectFromTokenIdentifier(token);
+        }
       } catch (ParseException e) {
         // NOP: subject remains null -> SC_FORBIDDEN will be returned
       }
diff --git 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
index 3ea7021..688a2ab 100644
--- 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
+++ 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
@@ -32,6 +32,9 @@ public interface JWTMessages {
   @Message( level = MessageLevel.INFO, text = "Access token {0} ({1}) has 
expired; a new one must be acquired." )
   void tokenHasExpired(String tokenDisplayText, String tokenId);
 
+  @Message( level = MessageLevel.INFO, text = "Access token {0} has expired; a 
new one must be acquired." )
+  void tokenHasExpired(String tokenId);
+
   @Message( level = MessageLevel.INFO, text = "The NotBefore check failed." )
   void notBeforeCheckFailed();
 
diff --git 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
index 2f1b864..c1a1056 100644
--- 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
+++ 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
@@ -58,6 +58,7 @@ import org.apache.knox.gateway.security.PrimaryPrincipal;
 import org.apache.knox.gateway.services.GatewayServices;
 import org.apache.knox.gateway.services.ServiceType;
 import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
+import org.apache.knox.gateway.services.security.token.TokenMetadata;
 import org.apache.knox.gateway.services.security.token.TokenServiceException;
 import org.apache.knox.gateway.services.security.token.TokenStateService;
 import org.apache.knox.gateway.services.security.token.TokenUtils;
@@ -174,11 +175,9 @@ public abstract class AbstractJWTFilter implements Filter {
     return audList;
   }
 
-  protected boolean tokenIsStillValid(JWT jwtToken) throws 
UnknownTokenException {
-    Date expires;
-    if (tokenStateService != null) {
-      expires = new Date(tokenStateService.getTokenExpiration(jwtToken));
-    } else {
+  protected boolean tokenIsStillValid(final JWT jwtToken) throws 
UnknownTokenException {
+    Date expires = 
getServerManagedStateExpiration(TokenUtils.getTokenId(jwtToken));
+    if (expires == null) {
       // if there is no expiration date then the lifecycle is tied entirely to
       // the cookie validity - otherwise ensure that the current time is before
       // the designated expiration time
@@ -187,6 +186,22 @@ public abstract class AbstractJWTFilter implements Filter {
     return expires == null || new Date().before(expires);
   }
 
+  protected boolean tokenIsStillValid(final String tokenId) throws 
UnknownTokenException {
+    Date expires = getServerManagedStateExpiration(tokenId);
+    return expires == null || (new Date().before(expires));
+  }
+
+  private Date getServerManagedStateExpiration(final String tokenId) throws 
UnknownTokenException {
+    Date expires = null;
+    if (tokenStateService != null) {
+      long value = tokenStateService.getTokenExpiration(tokenId);
+      if (value > 0) {
+        expires = new Date(value);
+      }
+    }
+    return expires;
+  }
+
   /**
    * Validate whether any of the accepted audience claims is present in the
    * issued token claims list for audience. Override this method in subclasses
@@ -196,7 +211,7 @@ public abstract class AbstractJWTFilter implements Filter {
    *          the JWT token where the allowed audiences will be found
    * @return true if an expected audience is present, otherwise false
    */
-  protected boolean validateAudiences(JWT jwtToken) {
+  protected boolean validateAudiences(final JWT jwtToken) {
     boolean valid = false;
 
     String[] tokenAudienceList = jwtToken.getAudienceClaims();
@@ -220,7 +235,7 @@ public abstract class AbstractJWTFilter implements Filter {
     return valid;
   }
 
-  protected void continueWithEstablishedSecurityContext(Subject subject, final 
HttpServletRequest request, final HttpServletResponse response, final 
FilterChain chain) throws IOException, ServletException {
+  protected void continueWithEstablishedSecurityContext(final Subject subject, 
final HttpServletRequest request, final HttpServletResponse response, final 
FilterChain chain) throws IOException, ServletException {
     Principal principal = (Principal) 
subject.getPrincipals(PrimaryPrincipal.class).toArray()[0];
     AuditContext context = auditService.getContext();
     if (context != null) {
@@ -257,24 +272,42 @@ public abstract class AbstractJWTFilter implements Filter 
{
     }
   }
 
-  public Subject createSubjectFromToken(String token) throws ParseException {
+  public Subject createSubjectFromToken(final String token) throws 
ParseException {
     return createSubjectFromToken(new JWTToken(token));
   }
 
-  protected Subject createSubjectFromToken(JWT token) {
+  protected Subject createSubjectFromToken(final JWT token) {
     String principal = token.getSubject();
     String claimvalue = null;
     if (expectedPrincipalClaim != null) {
       claimvalue = token.getClaim(expectedPrincipalClaim);
     }
+    // The newly constructed Sets check whether this Subject has been set 
read-only
+    // before permitting subsequent modifications. The newly created Sets also 
prevent
+    // illegal modifications by ensuring that callers have sufficient 
permissions.
+    //
+    // To modify the Principals Set, the caller must have 
AuthPermission("modifyPrincipals").
+    // To modify the public credential Set, the caller must have 
AuthPermission("modifyPublicCredentials").
+    // To modify the private credential Set, the caller must have 
AuthPermission("modifyPrivateCredentials").
+    return createSubjectFromTokenData(principal, claimvalue);
+  }
 
-    if (claimvalue != null) {
-      principal = claimvalue.toLowerCase(Locale.ROOT);
+  public Subject createSubjectFromTokenIdentifier(final String tokenId) {
+    TokenMetadata metadata = tokenStateService.getTokenMetadata(tokenId);
+    if (metadata != null) {
+      return createSubjectFromTokenData(metadata.getUserName(), null);
     }
+    return null;
+  }
+
+  protected Subject createSubjectFromTokenData(final String principal, final 
String expectedPrincipalClaimValue) {
+    String claimValue =
+              (expectedPrincipalClaimValue != null) ? 
expectedPrincipalClaimValue.toLowerCase(Locale.ROOT) : null;
+
     @SuppressWarnings("rawtypes")
     HashSet emptySet = new HashSet();
     Set<Principal> principals = new HashSet<>();
-    Principal p = new PrimaryPrincipal(principal);
+    Principal p = new PrimaryPrincipal(claimValue != null ? claimValue : 
principal);
     principals.add(p);
 
     // The newly constructed Sets check whether this Subject has been set 
read-only
@@ -287,8 +320,9 @@ public abstract class AbstractJWTFilter implements Filter {
     return new Subject(true, principals, emptySet, emptySet);
   }
 
-  protected boolean validateToken(HttpServletRequest request, 
HttpServletResponse response,
-      FilterChain chain, JWT token)
+
+  protected boolean validateToken(final HttpServletRequest request, final 
HttpServletResponse response,
+      final FilterChain chain, final JWT token)
       throws IOException, ServletException {
     final String tokenId = TokenUtils.getTokenId(token);
     final String displayableToken = 
Tokens.getTokenDisplayText(token.toString());
@@ -320,7 +354,7 @@ public abstract class AbstractJWTFilter implements Filter {
                     "Bad request: missing required token audience");
           }
         } else {
-          log.tokenHasExpired(tokenId, displayableToken);
+          log.tokenHasExpired(displayableToken, tokenId);
 
           // Explicitly evict the record of this token's signature 
verification (if present).
           // There is no value in keeping this record for expired tokens, and 
explicitly removing them may prevent
@@ -342,7 +376,31 @@ public abstract class AbstractJWTFilter implements Filter {
     return false;
   }
 
-  protected boolean verifyTokenSignature(final JWT token) {
+  protected boolean validateToken(final HttpServletRequest request,
+                                  final HttpServletResponse response,
+                                  final FilterChain chain,
+                                  final String tokenId)
+          throws IOException, ServletException {
+
+    if (tokenStateService != null) {
+      try {
+        if (tokenIsStillValid(tokenId)) {
+          return true;
+        } else {
+          log.tokenHasExpired(tokenId);
+          handleValidationError(request, response, 
HttpServletResponse.SC_BAD_REQUEST,
+                                "Bad request: token has expired");
+        }
+      } catch (UnknownTokenException e) {
+        log.unableToVerifyExpiration(e);
+        handleValidationError(request, response, 
HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
+      }
+    }
+
+    return false;
+  }
+
+    protected boolean verifyTokenSignature(final JWT token) {
     boolean verified;
 
     String tokenId = TokenUtils.getTokenId(token);
diff --git 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
index 467bfb7..1255704 100644
--- 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
+++ 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
@@ -17,6 +17,7 @@
  */
 package org.apache.knox.gateway.provider.federation.jwt.filter;
 
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.knox.gateway.services.security.token.impl.JWTToken;
 import org.apache.knox.gateway.util.CertificateUtils;
 import org.apache.knox.gateway.services.security.token.impl.JWT;
@@ -38,14 +39,19 @@ import java.util.Locale;
 
 public class JWTFederationFilter extends AbstractJWTFilter {
 
+  public enum TokenType {
+    JWT, Passcode;
+  }
+
   public static final String KNOX_TOKEN_AUDIENCES = "knox.token.audiences";
   public static final String TOKEN_VERIFICATION_PEM = 
"knox.token.verification.pem";
   public static final String KNOX_TOKEN_QUERY_PARAM_NAME = 
"knox.token.query.param.name";
   public static final String TOKEN_PRINCIPAL_CLAIM = 
"knox.token.principal.claim";
   public static final String JWKS_URL = "knox.token.jwks.url";
-  private static final String BEARER = "Bearer ";
-  private static final String BASIC = "Basic";
-  private static final String TOKEN = "Token";
+  public static final String BEARER   = "Bearer ";
+  public static final String BASIC    = "Basic";
+  public static final String TOKEN    = "Token";
+  public static final String PASSCODE = "Passcode";
   private String paramName;
 
   @Override
@@ -93,55 +99,74 @@ public class JWTFederationFilter extends AbstractJWTFilter {
   @Override
   public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain)
       throws IOException, ServletException {
-    final String wireToken = getWireToken(request);
+    final Pair<TokenType, String> wireToken = getWireToken(request);
 
     if (wireToken != null) {
-      try {
-        JWT token = new JWTToken(wireToken);
-        if (validateToken((HttpServletRequest)request, 
(HttpServletResponse)response, chain, token)) {
-          Subject subject = createSubjectFromToken(token);
-          continueWithEstablishedSecurityContext(subject, 
(HttpServletRequest)request, (HttpServletResponse)response, chain);
+      TokenType tokenType  = wireToken.getLeft();
+      String    tokenValue = wireToken.getRight();
+
+      if (TokenType.JWT.equals(tokenType)) {
+        try {
+          JWT token = new JWTToken(tokenValue);
+          if (validateToken((HttpServletRequest) request, 
(HttpServletResponse) response, chain, token)) {
+            Subject subject = createSubjectFromToken(token);
+            continueWithEstablishedSecurityContext(subject, 
(HttpServletRequest) request, (HttpServletResponse) response, chain);
+          }
+        } catch (ParseException ex) {
+          ((HttpServletResponse) 
response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
+        }
+      } else if (TokenType.Passcode.equals(tokenType)) {
+        // Validate the token based on the server-managed metadata
+        if (validateToken((HttpServletRequest) request, (HttpServletResponse) 
response, chain, tokenValue)) {
+          Subject subject = createSubjectFromTokenIdentifier(tokenValue);
+          continueWithEstablishedSecurityContext(subject, (HttpServletRequest) 
request, (HttpServletResponse) response, chain);
         }
-      } catch (ParseException ex) {
-        ((HttpServletResponse) 
response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
       }
-    }
-    else {
+    } else {
       // no token provided in header
       ((HttpServletResponse) 
response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
     }
   }
 
-  public String getWireToken(final ServletRequest request) {
+  public Pair<TokenType, String> getWireToken(final ServletRequest request) {
+      Pair<TokenType, String> parsed = null;
       String token = null;
       final String header = 
((HttpServletRequest)request).getHeader("Authorization");
       if (header != null) {
           if (header.startsWith(BEARER)) {
               // what follows the bearer designator should be the JWT token 
being used
-            // to request or as an access token
+              // to request or as an access token
               token = header.substring(BEARER.length());
-          }
-          else if 
(header.toLowerCase(Locale.ROOT).startsWith(BASIC.toLowerCase(Locale.ROOT))) {
-              // what follows the Basic designator should be the JWT token 
being used
-            // to request or as an access token
-              token = this.parseFromHTTPBasicCredentials(token, header);
+              parsed = Pair.of(TokenType.JWT, token);
+          } else if 
(header.toLowerCase(Locale.ROOT).startsWith(BASIC.toLowerCase(Locale.ROOT))) {
+              // what follows the Basic designator should be the JWT token or 
the unique token ID being used
+              // to request or as an access token
+              parsed = parseFromHTTPBasicCredentials(header);
           }
       }
-      if (token == null) {
+
+      if (parsed == null) {
           token = request.getParameter(this.paramName);
+          if (token != null) {
+            parsed = Pair.of(TokenType.JWT, token);
+          }
       }
-      return token;
+
+      return parsed;
   }
 
-  private String parseFromHTTPBasicCredentials(String token, final String 
header) {
+    private Pair<TokenType, String> parseFromHTTPBasicCredentials(final String 
header) {
+      Pair<TokenType, String> parsed = null;
       final String base64Credentials = header.substring(BASIC.length()).trim();
       final byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
       final String credentials = new String(credDecoded, 
StandardCharsets.UTF_8);
       final String[] values = credentials.split(":", 2);
-      if (values[0].equalsIgnoreCase(TOKEN)) {
-          token = values[1];
+      String username = values[0];
+      if (TOKEN.equalsIgnoreCase(username) || 
PASSCODE.equalsIgnoreCase(username)) {
+          parsed = Pair.of(TOKEN.equalsIgnoreCase(username) ? TokenType.JWT : 
TokenType.Passcode, values[1]);
       }
-      return token;
+
+      return parsed;
   }
 
   @Override
diff --git 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
index 3f6bc98..6d71a18 100644
--- 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
+++ 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
@@ -64,10 +64,8 @@ import java.security.cert.Certificate;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.text.MessageFormat;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.Enumeration;
-import java.util.List;
 import java.util.Locale;
 import java.util.Properties;
 import java.util.Set;
@@ -129,33 +127,18 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled );
       Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
       Assert.assertEquals("Not the expected principal", "alice", 
((Principal)principals.toArray()[0]).getName());
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -175,33 +158,18 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled );
       Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
       Assert.assertEquals("Not the expected principal", "alice", 
((Principal)principals.toArray()[0]).getName());
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -223,31 +191,16 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
-      Assert.assertTrue("doFilterCalled should not be true.", 
!chain.doFilterCalled);
+      Assert.assertFalse("doFilterCalled should not be true.", 
chain.doFilterCalled);
       Assert.assertNull("No Subject should be returned.", chain.subject);
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -267,33 +220,18 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled );
       Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
       Assert.assertEquals("Not the expected principal", "alice", 
((Principal)principals.toArray()[0]).getName());
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -313,31 +251,16 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
-      Assert.assertTrue("doFilterCalled should not be true.", 
!chain.doFilterCalled);
+      Assert.assertFalse("doFilterCalled should not be true.", 
chain.doFilterCalled);
       Assert.assertNull("No Subject should be returned.", chain.subject);
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -356,33 +279,18 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled );
       Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
       Assert.assertEquals("Not the expected principal", "alice", 
((Principal)principals.toArray()[0]).getName());
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -402,33 +310,18 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled );
       Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
       Assert.assertEquals("Not the expected principal", "alice", 
((Principal)principals.toArray()[0]).getName());
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -451,33 +344,18 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled );
       Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
       Assert.assertEquals("Not the expected principal", "alice", 
((Principal)principals.toArray()[0]).getName());
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -496,31 +374,16 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
-      Assert.assertTrue("doFilterCalled should not be false.", 
!chain.doFilterCalled);
+      Assert.assertFalse("doFilterCalled should not be false.", 
chain.doFilterCalled);
       Assert.assertNull("No Subject should be returned.", chain.subject);
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -538,33 +401,18 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL).anyTimes();
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL).anyTimes();
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled );
       Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
       Assert.assertEquals("Not the expected principal", "alice", 
((Principal)principals.toArray()[0]).getName());
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -583,31 +431,16 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setGarbledTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL).anyTimes();
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL).anyTimes();
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
-      Assert.assertTrue("doFilterCalled should not be true.", 
!chain.doFilterCalled);
+      Assert.assertFalse("doFilterCalled should not be true.", 
chain.doFilterCalled);
       Assert.assertNull("No Subject should be returned.", chain.subject);
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -635,31 +468,16 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL).anyTimes();
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL).anyTimes();
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
-      Assert.assertTrue("doFilterCalled should not be true.", 
!chain.doFilterCalled);
+      Assert.assertFalse("doFilterCalled should not be true.", 
chain.doFilterCalled);
       Assert.assertNull("No Subject should be returned.", chain.subject);
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -691,25 +509,11 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
       
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
@@ -732,31 +536,16 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-         new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
-      Assert.assertTrue("doFilterCalled should not be true.", 
!chain.doFilterCalled);
+      Assert.assertFalse("doFilterCalled should not be true.", 
chain.doFilterCalled);
       Assert.assertNull("No Subject should be returned.", chain.subject);
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -775,33 +564,18 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled);
       Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
       Assert.assertEquals("Not the expected principal", "alice", 
((Principal)principals.toArray()[0]).getName());
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -825,33 +599,18 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled );
       Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
       Assert.assertEquals("Not the expected principal", "alice", 
((Principal)principals.toArray()[0]).getName());
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -876,31 +635,16 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
-      Assert.assertTrue("doFilterCalled should not be false.", 
!chain.doFilterCalled );
+      Assert.assertFalse("doFilterCalled should not be false.", 
chain.doFilterCalled );
       Assert.assertNull("No Subject should be returned.", chain.subject);
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -921,31 +665,16 @@ public abstract class AbstractJWTFilterTest  {
       HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
 
-      EasyMock.expect(request.getRequestURL()).andReturn(
-          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
-      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
-          SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
-      Assert.assertTrue("doFilterCalled should not be false.", 
!chain.doFilterCalled);
+      Assert.assertFalse("doFilterCalled should not be false.", 
chain.doFilterCalled);
       Assert.assertNull("No Subject should be returned.", chain.subject);
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
@@ -987,20 +716,7 @@ public abstract class AbstractJWTFilterTest  {
       EasyMock.expect(request.getQueryString()).andReturn(null);
       HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
       
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
-      EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-        @Override
-        public void write(int b) {
-        }
-
-        @Override
-        public void setWriteListener(WriteListener arg0) {
-        }
-
-        @Override
-        public boolean isReady() {
-          return false;
-        }
-      }).anyTimes();
+      
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
       EasyMock.replay(request, response);
 
       doTestVerificationOptimization(request, response, principalAlice);
@@ -1100,20 +816,7 @@ public abstract class AbstractJWTFilterTest  {
   private HttpServletResponse createMockResponse() throws Exception {
     HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
     
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
-    EasyMock.expect(response.getOutputStream()).andAnswer(() -> new 
ServletOutputStream() {
-      @Override
-      public void write(int b) {
-      }
-
-      @Override
-      public void setWriteListener(WriteListener arg0) {
-      }
-
-      @Override
-      public boolean isReady() {
-        return false;
-      }
-    }).anyTimes();
+    
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
     return response;
   }
 
@@ -1136,7 +839,7 @@ public abstract class AbstractJWTFilterTest  {
     handler.doFilter(request, response, chain);
     Assert.assertTrue("doFilterCalled should not be false.", 
chain.doFilterCalled );
     Set<PrimaryPrincipal> principals = 
chain.subject.getPrincipals(PrimaryPrincipal.class);
-    Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+    Assert.assertFalse("No PrimaryPrincipal", principals.isEmpty());
     Assert.assertEquals("Not the expected principal", expectedPrincipal, 
((Principal)principals.toArray()[0]).getName());
   }
 
@@ -1179,20 +882,15 @@ public abstract class AbstractJWTFilterTest  {
   protected SignedJWT getJWT(String issuer, String sub, String aud, Date 
expires, Date nbf, RSAPrivateKey privateKey,
                              String signatureAlgorithm)
       throws Exception {
-    List<String> audiences = new ArrayList<>();
-    if (aud != null) {
-      audiences.add(aud);
-    }
-
     JWTClaimsSet claims = new JWTClaimsSet.Builder()
-    .issuer(issuer)
-    .subject(sub)
-    .audience(aud)
-    .expirationTime(expires)
-    .notBeforeTime(nbf)
-    .claim("scope", "openid")
-    .claim(JWTToken.KNOX_ID_CLAIM, String.valueOf(UUID.randomUUID()))
-    .build();
+                                          .issuer(issuer)
+                                          .subject(sub)
+                                          .audience(aud)
+                                          .expirationTime(expires)
+                                          .notBeforeTime(nbf)
+                                          .claim("scope", "openid")
+                                          .claim(JWTToken.KNOX_ID_CLAIM, 
String.valueOf(UUID.randomUUID()))
+                                          .build();
 
     JWSHeader header = new 
JWSHeader.Builder(JWSAlgorithm.parse(signatureAlgorithm)).build();
 
@@ -1285,4 +983,19 @@ public abstract class AbstractJWTFilterTest  {
     int getVerificationCount();
   }
 
+  static class DummyServletOutputStream extends ServletOutputStream {
+      @Override
+      public void write(int b) {
+      }
+
+      @Override
+      public void setWriteListener(WriteListener arg0) {
+      }
+
+      @Override
+      public boolean isReady() {
+        return false;
+      }
+  }
+
 }
diff --git 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/CommonJWTFilterTest.java
 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/CommonJWTFilterTest.java
index 35bb310..7e8353a 100644
--- 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/CommonJWTFilterTest.java
+++ 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/CommonJWTFilterTest.java
@@ -131,6 +131,9 @@ public class CommonJWTFilterTest {
     EasyMock.expect(tss.getTokenExpiration(anyObject(JWT.class)))
             .andThrow(new UnknownTokenException("eyjhbgcioi1234567890neg"))
             .anyTimes();
+    EasyMock.expect(tss.getTokenExpiration(anyObject(String.class)))
+            .andThrow(new UnknownTokenException("eyjhbgcioi1234567890neg"))
+            .anyTimes();
     EasyMock.replay(tss);
 
     doTestIsStillValid(tss);
@@ -141,6 +144,9 @@ public class CommonJWTFilterTest {
     EasyMock.expect(tss.getTokenExpiration(anyObject(JWT.class)))
             .andReturn(expiration)
             .anyTimes();
+    EasyMock.expect(tss.getTokenExpiration(anyObject(String.class)))
+            .andReturn(expiration)
+            .anyTimes();
     EasyMock.replay(tss);
     return doTestIsStillValid(tss);
   }
diff --git 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTAsHTTPBasicCredsFederationFilterTest.java
 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTAsHTTPBasicCredsFederationFilterTest.java
index b9b52fd..dbfe98f 100644
--- 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTAsHTTPBasicCredsFederationFilterTest.java
+++ 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTAsHTTPBasicCredsFederationFilterTest.java
@@ -17,18 +17,29 @@
  */
 package org.apache.knox.gateway.provider.federation;
 
+import 
org.apache.knox.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
 import 
org.apache.knox.gateway.provider.federation.jwt.filter.JWTFederationFilter;
-import org.junit.Test;
 import org.easymock.EasyMock;
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Properties;
+
 import com.nimbusds.jwt.SignedJWT;
+
+import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Assert;
 import org.junit.Before;
-import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+
+public class JWTAsHTTPBasicCredsFederationFilterTest extends 
AbstractJWTFilterTest {
 
-public class JWTAsHTTPBasicCredsFederationFilterTest extends 
AbstractJWTFilterTest
-{
     @Before
     public void setUp() {
       handler = new TestJWTFederationFilter();
@@ -36,48 +47,67 @@ public class JWTAsHTTPBasicCredsFederationFilterTest 
extends AbstractJWTFilterTe
     }
 
     @Override
+    protected String getAudienceProperty() {
+        return TestJWTFederationFilter.KNOX_TOKEN_AUDIENCES;
+    }
+
+    @Override
+    protected String getVerificationPemProperty() {
+        return TestJWTFederationFilter.TOKEN_VERIFICATION_PEM;
+    }
+
+    @Override
     protected void setTokenOnRequest(final HttpServletRequest request, final 
SignedJWT jwt) {
-        final String token = "Basic " + 
Base64.getEncoder().encodeToString(("Token:" + 
jwt.serialize()).getBytes(StandardCharsets.UTF_8));
-        
EasyMock.expect((Object)request.getHeader("Authorization")).andReturn((Object)token);
+        setTokenOnRequest(request, TestJWTFederationFilter.TOKEN, 
jwt.serialize());
     }
 
     @Override
     protected void setGarbledTokenOnRequest(final HttpServletRequest request, 
final SignedJWT jwt) {
-        final String token = "Basic " + 
Base64.getEncoder().encodeToString(("Token: ljm" + 
jwt.serialize()).getBytes(StandardCharsets.UTF_8));
-        
EasyMock.expect((Object)request.getHeader("Authorization")).andReturn((Object)token);
+        setTokenOnRequest(request, TestJWTFederationFilter.TOKEN, "ljm" + 
jwt.serialize());
     }
 
-    @Override
-    protected String getAudienceProperty() {
-        return "knox.token.audiences";
+    /**
+     * Bind the specified JWT to the specified request, and apply the 
specified authUsername as the HTTP Basic
+     * username in the Authorization header value.
+     *
+     * @param request      The request to which the JWT should be bound.
+     * @param authUsername The HTTP Basic auth username to apply in the 
Authorization header value.
+     * @param authPassword The HTTP Basic auth password to apply in the 
Authorization header value.
+     */
+    protected void setTokenOnRequest(final HttpServletRequest request,
+                                     final String             authUsername,
+                                     final String             authPassword) {
+        final byte[] basicAuth = (authUsername + ":" + 
authPassword).getBytes(StandardCharsets.UTF_8);
+        final String authHeaderValue = "Basic " + 
Base64.getEncoder().encodeToString(basicAuth);
+        
EasyMock.expect((Object)request.getHeader("Authorization")).andReturn(authHeaderValue);
     }
 
-    private static class TestJWTFederationFilter extends JWTFederationFilter 
implements TokenVerificationCounter {
-      private int verifiedCount;
+    @Test
+    public void testAlternativeCaseUsername() throws Exception {
+        try {
+            Properties props = getProperties();
+            handler.init(new TestFilterConfig(props));
 
-      void setTokenService(JWTokenAuthority ts) {
-        authority = ts;
-      }
+            SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "bob",
+                                   new Date(new Date().getTime() + 5000), 
privateKey);
 
-      @Override
-      protected void recordSignatureVerification(String tokenId) {
-        super.recordSignatureVerification(tokenId);
-        verifiedCount++;
-      }
+            HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
+            setTokenOnRequest(request, 
JWTFederationFilter.TOKEN.toLowerCase(Locale.ROOT), jwt.serialize());
 
-      @Override
-      public int getVerificationCount() {
-        return verifiedCount;
-      }
-    }
+            EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
+            EasyMock.expect(request.getQueryString()).andReturn(null);
+            HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
+            
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+            
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
+            EasyMock.replay(request, response);
 
-    @Override
-    protected String getVerificationPemProperty() {
-        return "knox.token.verification.pem";
+            TestFilterChain chain = new TestFilterChain();
+            handler.doFilter(request, response, chain);
+            Assert.assertTrue("doFilterCalled should be true.", 
chain.doFilterCalled );
+        } catch (ServletException se) {
+            fail("Should NOT have thrown a ServletException.");
+        }
     }
 
-    @Test
-    public void doTest() {
-    }
 }
 
diff --git 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
index ca8b53b..df8cd51 100644
--- 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
+++ 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
@@ -18,13 +18,12 @@
 package org.apache.knox.gateway.provider.federation;
 
 import com.nimbusds.jwt.SignedJWT;
-import 
org.apache.knox.gateway.provider.federation.jwt.filter.JWTFederationFilter;
-import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 import org.easymock.EasyMock;
 import org.junit.Before;
 
 import javax.servlet.http.HttpServletRequest;
 
+@SuppressWarnings("PMD.TestClassWithoutTestCases")
 public class JWTFederationFilterTest extends AbstractJWTFilterTest {
   @Before
   public void setUp() {
@@ -33,44 +32,24 @@ public class JWTFederationFilterTest extends 
AbstractJWTFilterTest {
   }
 
   @Override
-  protected void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt) {
-    String token = "Bearer " + jwt.serialize();
-    EasyMock.expect(request.getHeader("Authorization")).andReturn(token);
+  protected String getAudienceProperty() {
+    return TestJWTFederationFilter.KNOX_TOKEN_AUDIENCES;
   }
 
   @Override
-  protected void setGarbledTokenOnRequest(HttpServletRequest request, 
SignedJWT jwt) {
-    String token = "Bearer " + "ljm" + jwt.serialize();
-    EasyMock.expect(request.getHeader("Authorization")).andReturn(token);
+  protected String getVerificationPemProperty() {
+    return TestJWTFederationFilter.TOKEN_VERIFICATION_PEM;
   }
 
   @Override
-  protected String getAudienceProperty() {
-    return TestJWTFederationFilter.KNOX_TOKEN_AUDIENCES;
-  }
-
-  private static class TestJWTFederationFilter extends JWTFederationFilter 
implements TokenVerificationCounter {
-    private int verificationCount;
-
-    void setTokenService(final JWTokenAuthority ts) {
-      authority = ts;
-    }
-
-    @Override
-    protected void recordSignatureVerification(final String tokenId) {
-      super.recordSignatureVerification(tokenId);
-      verificationCount++;
-    }
-
-    @Override
-    public int getVerificationCount() {
-      return verificationCount;
-    }
+  protected void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt) {
+    String token = TestJWTFederationFilter.BEARER + " " + jwt.serialize();
+    EasyMock.expect(request.getHeader("Authorization")).andReturn(token);
   }
 
   @Override
-  protected String getVerificationPemProperty() {
-    return TestJWTFederationFilter.TOKEN_VERIFICATION_PEM;
+  protected void setGarbledTokenOnRequest(HttpServletRequest request, 
SignedJWT jwt) {
+    String token = TestJWTFederationFilter.BEARER + " ljm" + jwt.serialize();
+    EasyMock.expect(request.getHeader("Authorization")).andReturn(token);
   }
-
 }
diff --git 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TestJWTFederationFilter.java
 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TestJWTFederationFilter.java
new file mode 100644
index 0000000..e895ba0
--- /dev/null
+++ 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TestJWTFederationFilter.java
@@ -0,0 +1,56 @@
+/*
+ *
+ *  * 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.knox.gateway.provider.federation;
+
+import 
org.apache.knox.gateway.provider.federation.jwt.filter.JWTFederationFilter;
+import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
+import org.apache.knox.gateway.services.security.token.TokenStateService;
+
+import java.lang.reflect.Field;
+
+public class TestJWTFederationFilter extends JWTFederationFilter
+                                     implements 
AbstractJWTFilterTest.TokenVerificationCounter {
+    private int verifiedCount;
+
+    void setTokenService(JWTokenAuthority ts) {
+        authority = ts;
+    }
+
+    void setTokenStateService(final TokenStateService tss) {
+        try {
+            Field tssField = 
getClass().getSuperclass().getSuperclass().getDeclaredField("tokenStateService");
+            tssField.setAccessible(true);
+            tssField.set(this, tss);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    protected void recordSignatureVerification(String tokenId) {
+        super.recordSignatureVerification(tokenId);
+        verifiedCount++;
+    }
+
+    @Override
+    public int getVerificationCount() {
+        return verifiedCount;
+    }
+
+}
diff --git 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java
 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java
new file mode 100644
index 0000000..3c548e9
--- /dev/null
+++ 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java
@@ -0,0 +1,419 @@
+/*
+ *
+ *  * 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.knox.gateway.provider.federation;
+
+import com.nimbusds.jwt.SignedJWT;
+import org.apache.knox.gateway.config.GatewayConfig;
+import 
org.apache.knox.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
+import 
org.apache.knox.gateway.provider.federation.jwt.filter.JWTFederationFilter;
+import org.apache.knox.gateway.services.ServiceLifecycleException;
+import org.apache.knox.gateway.services.security.token.TokenMetadata;
+import org.apache.knox.gateway.services.security.token.TokenStateService;
+import org.apache.knox.gateway.services.security.token.TokenUtils;
+import org.apache.knox.gateway.services.security.token.UnknownTokenException;
+import org.apache.knox.gateway.services.security.token.impl.JWT;
+import org.apache.knox.gateway.services.security.token.impl.JWTToken;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.fail;
+
+@SuppressWarnings({"PMD.JUnit4TestShouldUseBeforeAnnotation", 
"PMD.JUnit4TestShouldUseTestAnnotation"})
+public class TokenIDAsHTTPBasicCredsFederationFilterTest extends 
JWTAsHTTPBasicCredsFederationFilterTest {
+
+    TestTokenStateService tss;
+
+    @Override
+    public void setUp() {
+        super.setUp();
+        tss = new TestTokenStateService();
+        ((TestJWTFederationFilter) handler).setTokenStateService(tss);
+    }
+
+    @Override
+    protected void setTokenOnRequest(final HttpServletRequest request, final 
SignedJWT jwt) {
+        addTokenState(jwt);
+        setTokenOnRequest(request, TestJWTFederationFilter.PASSCODE, 
getTokenId(jwt));
+    }
+
+    /**
+     * Bind the specified JWT to the specified request, and apply the 
specified authUsername as the HTTP Basic
+     * username in the Authorization header value.
+     *
+     * @param request      The request to which the JWT should be bound.
+     * @param jwt          The JWT
+     * @param authUsername The HTTP Basic auth username to apply in the 
Authorization header value.
+     */
+    protected void setTokenOnRequest(final HttpServletRequest request,
+                                     final SignedJWT          jwt,
+                                     final String             authUsername) {
+        addTokenState(jwt);
+        setTokenOnRequest(request, authUsername, getTokenId(jwt));
+    }
+
+    @Override
+    protected void setGarbledTokenOnRequest(final HttpServletRequest request, 
final SignedJWT jwt) {
+        setTokenOnRequest(request, TestJWTFederationFilter.PASSCODE, "junk" + 
getTokenId(jwt));
+    }
+
+    private String getTokenId(final SignedJWT jwt) {
+        String tokenId = null;
+        try {
+             tokenId = (String) 
jwt.getJWTClaimsSet().getClaim(JWTToken.KNOX_ID_CLAIM);
+        } catch (ParseException e) {
+            //
+        }
+        return tokenId;
+    }
+
+    private void addTokenState(final SignedJWT jwt) {
+        try {
+            JWTToken token = new JWTToken(jwt.serialize());
+            tss.addToken(token, System.currentTimeMillis() - 
TimeUnit.MINUTES.toMillis(5));
+
+            String subject = (String) 
jwt.getJWTClaimsSet().getClaim(JWTToken.SUBJECT);
+            tss.addMetadata(TokenUtils.getTokenId(token), new 
TokenMetadata(subject));
+        } catch (ParseException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    @Override
+    public void testAlternativeCaseUsername() throws Exception {
+        try {
+            Properties props = getProperties();
+            handler.init(new TestFilterConfig(props));
+
+            SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "bob",
+                    new Date(new Date().getTime() + 5000), privateKey);
+
+            HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
+            setTokenOnRequest(request, jwt, 
JWTFederationFilter.PASSCODE.toLowerCase(Locale.ROOT));
+
+            EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
+            EasyMock.expect(request.getQueryString()).andReturn(null);
+            HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
+            
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+            
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
+            EasyMock.replay(request, response);
+
+            TestFilterChain chain = new TestFilterChain();
+            handler.doFilter(request, response, chain);
+            Assert.assertTrue("doFilterCalled should be true.", 
chain.doFilterCalled );
+        } catch (ServletException se) {
+            fail("Should NOT have thrown a ServletException.");
+        }
+    }
+
+    @Test
+    public void testInvalidUsername() throws Exception {
+        try {
+            Properties props = getProperties();
+            handler.init(new TestFilterConfig(props));
+
+            SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "bob",
+                                   new Date(new Date().getTime() + 5000), 
privateKey);
+
+            HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
+            setTokenOnRequest(request, jwt, "InvalidBasicAuthUsername");
+
+            EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
+            EasyMock.expect(request.getQueryString()).andReturn(null);
+            HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
+            
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+            
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
+            EasyMock.replay(request, response);
+
+            TestFilterChain chain = new TestFilterChain();
+            handler.doFilter(request, response, chain);
+            Assert.assertFalse("doFilterCalled should be false.", 
chain.doFilterCalled );
+        } catch (ServletException se) {
+            fail("Should NOT have thrown a ServletException.");
+        }
+    }
+
+    /**
+     * Negative test, specifying the Basic auth username as the one used for 
JWTs while specifying
+     * the passcode as the password.
+     */
+    @Test
+    public void testInvalidJWTForPasscode() throws Exception {
+        try {
+            Properties props = getProperties();
+            handler.init(new TestFilterConfig(props));
+
+            SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "bob",
+                            new Date(new Date().getTime() + 5000), privateKey);
+
+            HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
+            setTokenOnRequest(request, jwt, JWTFederationFilter.TOKEN);
+
+            EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
+            EasyMock.expect(request.getQueryString()).andReturn(null);
+            HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
+            
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+            
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
+            EasyMock.replay(request, response);
+
+            TestFilterChain chain = new TestFilterChain();
+            handler.doFilter(request, response, chain);
+            Assert.assertFalse("doFilterCalled should be false.", 
chain.doFilterCalled );
+        } catch (ServletException se) {
+            fail("Should NOT have thrown a ServletException.");
+        }
+    }
+
+    /**
+     * Negative test, specifying the Basic auth username as the one used for 
passcodes while specifying the JWT
+     * as the password.
+     */
+    @Test
+    public void testInvalidPasscodeForJWT() throws Exception {
+        try {
+            Properties props = getProperties();
+            handler.init(new TestFilterConfig(props));
+
+            SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "bob",
+                                   new Date(new Date().getTime() + 5000), 
privateKey);
+
+            HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
+            setTokenOnRequest(request, JWTFederationFilter.PASSCODE, 
jwt.serialize());
+
+            EasyMock.expect(request.getRequestURL()).andReturn(new 
StringBuffer(SERVICE_URL)).anyTimes();
+            EasyMock.expect(request.getQueryString()).andReturn(null);
+            HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
+            
EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
+            
EasyMock.expect(response.getOutputStream()).andAnswer(DummyServletOutputStream::new).anyTimes();
+            EasyMock.replay(request, response);
+
+            TestFilterChain chain = new TestFilterChain();
+            handler.doFilter(request, response, chain);
+            Assert.assertFalse("doFilterCalled should be false.", 
chain.doFilterCalled );
+            Assert.assertNull("Subject should be null since authentication 
should have failed.", chain.subject);
+        } catch (ServletException se) {
+            fail("Should NOT have thrown a ServletException.");
+        }
+    }
+
+    @Override
+    public void testInvalidAudienceJWT() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testNoTokenAudience() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testNoAudienceConfigured() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testEmptyAudienceConfigured() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testValidAudienceJWT() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testValidAudienceJWTWhitespace() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testValidIssuerViaConfig() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testInvalidIssuer() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testFailedSignatureValidationJWT() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testInvalidVerificationPEM() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testRS512SignatureAlgorithm() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testInvalidSignatureAlgorithm() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testValidVerificationPEM() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testNotBeforeJWT() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testVerificationOptimization() throws Exception {
+        // Override to disable N/A test
+    }
+
+    @Override
+    public void testExpiredTokensEvictedFromSignatureVerificationCache() 
throws Exception {
+        // Override to disable N/A test
+    }
+
+    /**
+     * Very basic TokenStateService implementation for these tests only
+     */
+    private static class TestTokenStateService implements TokenStateService {
+
+        private final Map<String, Long> tokenExpirations = new 
ConcurrentHashMap<>();
+        private final Map<String, TokenMetadata> tokenMetadata = new 
ConcurrentHashMap<>();
+
+        @Override
+        public void init(GatewayConfig config, Map<String, String> options) 
throws ServiceLifecycleException {
+        }
+
+        @Override
+        public void start() throws ServiceLifecycleException {
+        }
+
+        @Override
+        public void stop() throws ServiceLifecycleException {
+        }
+
+        @Override
+        public long getDefaultRenewInterval() {
+            return TimeUnit.DAYS.toMillis(1);
+        }
+
+        @Override
+        public long getDefaultMaxLifetimeDuration() {
+            return TimeUnit.DAYS.toMillis(7);
+        }
+
+        @Override
+        public void addToken(JWTToken token, long issueTime) {
+            String expiration = token.getExpires();
+            if (expiration == null || expiration.isEmpty()) {
+                expiration = "0";
+            }
+            tokenExpirations.put(TokenUtils.getTokenId(token), 
Long.parseLong(expiration));
+        }
+
+        @Override
+        public void addToken(String tokenId, long issueTime, long expiration) {
+            tokenExpirations.put(tokenId, expiration);
+        }
+
+        @Override
+        public void addToken(String tokenId, long issueTime, long expiration, 
long maxLifetimeDuration) {
+            tokenExpirations.put(tokenId, expiration);
+        }
+
+        @Override
+        public boolean isExpired(JWTToken token) throws UnknownTokenException {
+            Long expiration = 
tokenExpirations.get(TokenUtils.getTokenId(token));
+            return (new Date(expiration).before(new Date()));
+        }
+
+        @Override
+        public void revokeToken(JWTToken token) throws UnknownTokenException {
+
+        }
+
+        @Override
+        public void revokeToken(String tokenId) throws UnknownTokenException {
+
+        }
+
+        @Override
+        public long renewToken(JWTToken token) throws UnknownTokenException {
+            return 0;
+        }
+
+        @Override
+        public long renewToken(JWTToken token, long renewInterval) throws 
UnknownTokenException {
+            return 0;
+        }
+
+        @Override
+        public long renewToken(String tokenId) throws UnknownTokenException {
+            return 0;
+        }
+
+        @Override
+        public long renewToken(String tokenId, long renewInterval) throws 
UnknownTokenException {
+            return 0;
+        }
+
+        @Override
+        public long getTokenExpiration(JWT token) throws UnknownTokenException 
{
+            return getTokenExpiration(TokenUtils.getTokenId(token));
+        }
+
+        @Override
+        public long getTokenExpiration(String tokenId) throws 
UnknownTokenException {
+            if (!tokenExpirations.containsKey(tokenId)) {
+                throw new UnknownTokenException(tokenId);
+            }
+            return tokenExpirations.get(tokenId);
+        }
+
+        @Override
+        public long getTokenExpiration(String tokenId, boolean validate) 
throws UnknownTokenException {
+            return getTokenExpiration(tokenId);
+        }
+
+        @Override
+        public void addMetadata(String tokenId, TokenMetadata metadata) {
+            tokenMetadata.put(tokenId, metadata);
+        }
+
+        @Override
+        public TokenMetadata getTokenMetadata(String tokenId) {
+            return tokenMetadata.get(tokenId);
+        }
+    }
+}

Reply via email to