KNOX-2562 - TokenStateService getTokenMetadata method should throw UnknownTokenException (#426)

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 e2a5eb0..9cb8040 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
@@ -49,6 +49,7 @@
 import org.apache.knox.gateway.audit.api.Action;
 import org.apache.knox.gateway.audit.api.ActionOutcome;
 import org.apache.knox.gateway.audit.api.Auditor;
+import org.apache.knox.gateway.services.security.token.UnknownTokenException;
 
 public class HadoopAuthPostFilter implements Filter {
 
@@ -87,7 +88,7 @@
         } else if (JWTFederationFilter.TokenType.Passcode.equals(tokenType)) {
           subject = jwtFilter.createSubjectFromTokenIdentifier(token);
         }
-      } catch (ParseException e) {
+      } catch (ParseException | UnknownTokenException e) {
         // NOP: subject remains null -> SC_FORBIDDEN will be returned
       }
     } else {
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 fbbe3ce..2e37526 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
@@ -272,11 +272,11 @@
     }
   }
 
-  public Subject createSubjectFromToken(final String token) throws ParseException {
+  public Subject createSubjectFromToken(final String token) throws ParseException, UnknownTokenException {
     return createSubjectFromToken(new JWTToken(token));
   }
 
-  protected Subject createSubjectFromToken(final JWT token) {
+  protected Subject createSubjectFromToken(final JWT token) throws UnknownTokenException {
     String principal = token.getSubject();
     String claimvalue = null;
     if (expectedPrincipalClaim != null) {
@@ -292,7 +292,7 @@
     return createSubjectFromTokenData(principal, claimvalue);
   }
 
-  public Subject createSubjectFromTokenIdentifier(final String tokenId) {
+  public Subject createSubjectFromTokenIdentifier(final String tokenId) throws UnknownTokenException {
     TokenMetadata metadata = tokenStateService.getTokenMetadata(tokenId);
     if (metadata != null) {
       return createSubjectFromTokenData(metadata.getUserName(), null);
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 1255704..a8d50df 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
@@ -18,6 +18,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.UnknownTokenException;
 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;
@@ -112,14 +113,18 @@
             Subject subject = createSubjectFromToken(token);
             continueWithEstablishedSecurityContext(subject, (HttpServletRequest) request, (HttpServletResponse) response, chain);
           }
-        } catch (ParseException ex) {
+        } catch (ParseException | UnknownTokenException 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);
+          try {
+            Subject subject = createSubjectFromTokenIdentifier(tokenValue);
+            continueWithEstablishedSecurityContext(subject, (HttpServletRequest) request, (HttpServletResponse) response, chain);
+          } catch (UnknownTokenException e) {
+            ((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
+          }
         }
       }
     } else {
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
index 7cf2804..031c564 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
@@ -22,6 +22,7 @@
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 import org.apache.knox.gateway.provider.federation.jwt.JWTMessages;
 import org.apache.knox.gateway.security.PrimaryPrincipal;
+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.apache.knox.gateway.util.CertificateUtils;
@@ -175,7 +176,7 @@
             // we found a valid cookie we don't need to keep checking anymore
             return;
           }
-        } catch (ParseException ignore) {
+        } catch (ParseException | UnknownTokenException ignore) {
           // Ignore the error since cookie was invalid
           // Fall through to keep checking if there are more cookies
         }
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
index 3c548e9..a439143 100644
--- 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
@@ -412,7 +412,10 @@
         }
 
         @Override
-        public TokenMetadata getTokenMetadata(String tokenId) {
+        public TokenMetadata getTokenMetadata(String tokenId) throws UnknownTokenException {
+            if (!tokenMetadata.containsKey(tokenId)) {
+                throw new UnknownTokenException(tokenId);
+            }
             return tokenMetadata.get(tokenId);
         }
     }
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateService.java
index 4ac128a..63431dd 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateService.java
@@ -446,13 +446,22 @@
   }
 
   @Override
-  public TokenMetadata getTokenMetadata(String tokenId) {
-    TokenMetadata tokenMetadata = super.getTokenMetadata(tokenId);
+  public TokenMetadata getTokenMetadata(String tokenId) throws UnknownTokenException {
+    TokenMetadata tokenMetadata = null;
+    try {
+      tokenMetadata = super.getTokenMetadata(tokenId);
+    } catch (UnknownTokenException e) {
+      // This is expected if the metadata is not yet part of the in-memory record. In this case, the metadata will
+      // be retrieved from the alias store.
+    }
+
     if (tokenMetadata == null) {
       try {
         final char[] tokenMetadataAliasValue = getPasswordUsingAliasService(tokenId + TOKEN_META_POSTFIX);
         if (tokenMetadataAliasValue != null) {
           tokenMetadata = TokenMetadata.fromJSON(new String(tokenMetadataAliasValue));
+        } else {
+          throw new UnknownTokenException(tokenId);
         }
       } catch (AliasServiceException e) {
         log.errorAccessingTokenState(tokenId, e);
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java
index f08412d..b90a295 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java
@@ -385,7 +385,10 @@
   }
 
   @Override
-  public TokenMetadata getTokenMetadata(String tokenId) {
+  public TokenMetadata getTokenMetadata(String tokenId) throws UnknownTokenException {
+    if (!metadataMap.containsKey(tokenId)) {
+      throw new UnknownTokenException(tokenId);
+    }
     return metadataMap.get(tokenId);
   }
 }
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateServiceTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateServiceTest.java
index 7aa1fac..cff5506 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateServiceTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateServiceTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -109,6 +108,14 @@
     createTokenStateService().getTokenExpiration(TokenUtils.getTokenId(token), false);
   }
 
+  @Test(expected = UnknownTokenException.class)
+  public void testGetMetadata_InvalidToken() throws Exception {
+    final JWTToken token = createMockToken(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(60));
+
+    // Expecting an UnknownTokenException because the token is not known to the TokenStateService
+    createTokenStateService().getTokenMetadata(TokenUtils.getTokenId(token));
+  }
+
   @Test
   public void testGetExpiration_AfterRenewal() throws Exception {
     final JWTToken token = createMockToken(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(60));
@@ -267,13 +274,19 @@
     tss.getTokenExpiration(token);
   }
 
+  @SuppressWarnings("PMD.JUnitUseExpected")
   @Test
   public void testAddTokenMetadata() throws Exception {
     final JWT token = getJWTToken(System.currentTimeMillis());
     final String tokenId = token.getClaim(JWTToken.KNOX_ID_CLAIM);
     final TokenStateService tss = new DefaultTokenStateService();
     tss.addToken((JWTToken) token, System.currentTimeMillis());
-    assertNull(tss.getTokenMetadata(tokenId));
+    try {
+      tss.getTokenMetadata(tokenId);
+      fail("Expected exception since there is no metadata for the token ID.");
+    } catch (UnknownTokenException e) {
+      // Expected
+    }
 
     final String userName = "testUser";
     tss.addMetadata(token.getClaim(JWTToken.KNOX_ID_CLAIM), new TokenMetadata(userName));
diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index b9d57b0..4959ce9 100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -1135,7 +1135,7 @@
     }
 
     @Override
-    public TokenMetadata getTokenMetadata(String tokenId) {
+    public TokenMetadata getTokenMetadata(String tokenId) throws UnknownTokenException {
       return null;
     }
 
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java
index 736a272..ff1e3a9 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java
@@ -182,6 +182,6 @@
    *          The token's unique identifier.
    * @return The associated token metadata
    */
-  TokenMetadata getTokenMetadata(String tokenId);
+  TokenMetadata getTokenMetadata(String tokenId) throws UnknownTokenException;
 
 }