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 dcdeace  KNOX-2389 - AliasBasedTokenStateService stops processing 
persisted journal entries if one is malformed (#346)
dcdeace is described below

commit dcdeace47a179851ee0810d446d2ad903ac826c2
Author: Phil Zampino <[email protected]>
AuthorDate: Tue Jun 16 09:30:45 2020 -0400

    KNOX-2389 - AliasBasedTokenStateService stops processing persisted journal 
entries if one is malformed (#346)
---
 .../token/impl/AliasBasedTokenStateService.java    |  26 +++--
 .../token/impl/TokenStateServiceMessages.java      |   6 +
 .../token/impl/state/FileTokenStateJournal.java    |   6 +-
 .../impl/AliasBasedTokenStateServiceTest.java      | 126 +++++++++++++++++++++
 4 files changed, 152 insertions(+), 12 deletions(-)

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 24d3fb9..28ca807 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
@@ -74,17 +74,21 @@ public class AliasBasedTokenStateService extends 
DefaultTokenStateService {
       List<JournalEntry> entries = journal.get();
       for (JournalEntry entry : entries) {
         String id = entry.getTokenId();
-        long issueTime = Long.parseLong(entry.getIssueTime());
-        long expiration = Long.parseLong(entry.getExpiration());
-        long maxLifetime = Long.parseLong(entry.getMaxLifetime());
-
-        // Add the token state to memory
-        super.addToken(id, issueTime, expiration, maxLifetime);
-
-        synchronized (unpersistedState) {
-          // The max lifetime entry is added by way of the call to 
super.addToken(),
-          // so only need to add the expiration entry here.
-          unpersistedState.add(new TokenExpiration(id, expiration));
+        try {
+          long issueTime   = Long.parseLong(entry.getIssueTime());
+          long expiration  = Long.parseLong(entry.getExpiration());
+          long maxLifetime = Long.parseLong(entry.getMaxLifetime());
+
+          // Add the token state to memory
+          super.addToken(id, issueTime, expiration, maxLifetime);
+
+          synchronized (unpersistedState) {
+            // The max lifetime entry is added by way of the call to 
super.addToken(),
+            // so only need to add the expiration entry here.
+            unpersistedState.add(new TokenExpiration(id, expiration));
+          }
+        } catch (Exception e) {
+          log.failedToLoadJournalEntry(id, e);
         }
       }
     } catch (IOException e) {
diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
index a3d7502..735abc9 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
@@ -118,6 +118,12 @@ public interface TokenStateServiceMessages {
   @Message(level = MessageLevel.DEBUG, text = "Persisting token state journal 
entry as {0}")
   void persistingJournalEntry(String journalEntryFilename);
 
+  @Message(level = MessageLevel.ERROR, text = "Failed to load persisted token 
state journal entry for {0} : {1}")
+  void failedToLoadJournalEntry(String tokenId, @StackTrace(level = 
MessageLevel.DEBUG) Exception e);
+
+  @Message(level = MessageLevel.ERROR, text = "Failed to load persisted token 
state journal entry : {0}")
+  void failedToLoadJournalEntry(@StackTrace(level = MessageLevel.DEBUG) 
Exception e);
+
   @Message(level = MessageLevel.ERROR, text = "Failed to persisting token 
state journal entry for {0} : {1}")
   void failedToPersistJournalEntry(String tokenId, @StackTrace(level = 
MessageLevel.DEBUG) Exception e);
 
diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournal.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournal.java
index 31a8954..11dd702 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournal.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournal.java
@@ -108,7 +108,11 @@ abstract class FileTokenStateJournal implements 
TokenStateJournal {
             BufferedReader reader = new BufferedReader(new 
InputStreamReader(input, StandardCharsets.UTF_8));
             String line;
             while ((line = reader.readLine()) != null) {
-                entries.add(FileJournalEntry.parse(line));
+                try {
+                    entries.add(FileJournalEntry.parse(line));
+                } catch (Exception e) {
+                    log.failedToLoadJournalEntry(e);
+                }
             }
         }
 
diff --git 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateServiceTest.java
 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateServiceTest.java
index 6a08635..606b3aa 100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateServiceTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateServiceTest.java
@@ -22,6 +22,7 @@ import org.apache.knox.gateway.services.security.AliasService;
 import org.apache.knox.gateway.services.security.AliasServiceException;
 import org.apache.knox.gateway.services.security.token.TokenStateService;
 import org.apache.knox.gateway.services.security.token.impl.JWTToken;
+import org.apache.knox.gateway.services.token.state.JournalEntry;
 import org.apache.knox.gateway.services.token.state.TokenStateJournal;
 import 
org.apache.knox.gateway.services.token.impl.state.TokenStateJournalFactory;
 import org.easymock.EasyMock;
@@ -43,6 +44,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
@@ -564,6 +566,90 @@ public class AliasBasedTokenStateServiceTest extends 
DefaultTokenStateServiceTes
     EasyMock.verify(aliasService);
   }
 
+  @Test
+  public void testLoadTokenStateJournalDuringInitWithInvalidEntries() throws 
Exception {
+    final int TOKEN_COUNT = 5;
+
+    AliasService aliasService = EasyMock.createMock(AliasService.class);
+    aliasService.getAliasesForCluster(anyString());
+    EasyMock.expectLastCall().andReturn(Collections.emptyList()).anyTimes();
+    EasyMock.replay(aliasService);
+
+    // Create some test tokens
+    final Set<JWTToken> testTokens = new HashSet<>();
+    for (int i = 0; i < TOKEN_COUNT ; i++) {
+      JWTToken token = createMockToken(System.currentTimeMillis() - 
TimeUnit.SECONDS.toMillis(60));
+      testTokens.add(token);
+    }
+
+    // Persist the token state journal entries before initializing the 
TokenStateService
+    TokenStateJournal journal = 
TokenStateJournalFactory.create(createMockGatewayConfig(false));
+    for (JWTToken token : testTokens) {
+      journal.add(token.getClaim(JWTToken.KNOX_ID_CLAIM),
+                  System.currentTimeMillis(),
+                  token.getExpiresDate().getTime(),
+                  System.currentTimeMillis() + TimeUnit.HOURS.toMillis(24));
+    }
+
+    // Add an entry with an invalid token identifier
+    journal.add("   ",
+                System.currentTimeMillis(),
+                System.currentTimeMillis(),
+                System.currentTimeMillis());
+
+    // Add an entry with an invalid issue time
+    journal.add(new TestJournalEntry(UUID.randomUUID().toString(),
+                "invalidLongValue",
+                String.valueOf(System.currentTimeMillis()),
+                String.valueOf(System.currentTimeMillis())));
+
+    // Add an entry with an invalid expiration time
+    journal.add(new TestJournalEntry(UUID.randomUUID().toString(),
+                String.valueOf(System.currentTimeMillis()),
+                "invalidLongValue",
+                String.valueOf(System.currentTimeMillis())));
+
+    // Add an entry with an invalid max lifetime
+    journal.add(new TestJournalEntry(UUID.randomUUID().toString(),
+                                     
String.valueOf(System.currentTimeMillis()),
+                                     
String.valueOf(System.currentTimeMillis()),
+                                     "invalidLongValue"));
+
+    AliasBasedTokenStateService tss = new AliasBasedTokenStateService();
+    tss.setAliasService(aliasService);
+
+    // Initialize the service, and presumably load the previously-persisted 
journal entries
+    initTokenStateService(tss);
+
+    Field tokenExpirationsField = 
tss.getClass().getSuperclass().getDeclaredField("tokenExpirations");
+    tokenExpirationsField.setAccessible(true);
+    Map<String, Long> tokenExpirations = (Map<String, Long>) 
tokenExpirationsField.get(tss);
+
+    Field maxTokenLifetimesField = 
tss.getClass().getSuperclass().getDeclaredField("maxTokenLifetimes");
+    maxTokenLifetimesField.setAccessible(true);
+    Map<String, Long> maxTokenLifetimes = (Map<String, Long>) 
maxTokenLifetimesField.get(tss);
+
+    Field unpersistedStateField = 
tss.getClass().getDeclaredField("unpersistedState");
+    unpersistedStateField.setAccessible(true);
+    List<AliasBasedTokenStateService.TokenState> unpersistedState =
+            (List<AliasBasedTokenStateService.TokenState>) 
unpersistedStateField.get(tss);
+
+    assertEquals("Expected the tokens expirations to have been added in the 
base class cache.",
+                 TOKEN_COUNT,
+                 tokenExpirations.size());
+
+    assertEquals("Expected the tokens lifetimes to have been added in the base 
class cache.",
+                 TOKEN_COUNT,
+                 maxTokenLifetimes.size());
+
+    assertEquals("Expected the unpersisted state to have been added.",
+                 (TOKEN_COUNT * 2), // Two TokenState entries per token 
(expiration, max lifetime)
+                 unpersistedState.size());
+
+    // Verify that the expected methods were invoked
+    EasyMock.verify(aliasService);
+  }
+
   @Override
   protected TokenStateService createTokenStateService() throws Exception {
     AliasBasedTokenStateService tss = new AliasBasedTokenStateService();
@@ -718,4 +804,44 @@ public class AliasBasedTokenStateServiceTest extends 
DefaultTokenStateServiceTes
       }
     }
   }
+
+  private static class TestJournalEntry implements JournalEntry {
+
+    private String tokenId;
+    private String issueTime;
+    private String expiration;
+    private String maxLifetime;
+
+    TestJournalEntry(String tokenId, String issueTime, String expiration, 
String maxLifetime) {
+      this.tokenId     = tokenId;
+      this.issueTime   = issueTime;
+      this.expiration  = expiration;
+      this.maxLifetime = maxLifetime;
+    }
+
+    @Override
+    public String getTokenId() {
+      return tokenId;
+    }
+
+    @Override
+    public String getIssueTime() {
+      return issueTime;
+    }
+
+    @Override
+    public String getExpiration() {
+      return expiration;
+    }
+
+    @Override
+    public String getMaxLifetime() {
+      return maxLifetime;
+    }
+
+    @Override
+    public String toString() {
+      return tokenId + "," + issueTime + "," + expiration + "," + maxLifetime;
+    }
+  }
 }

Reply via email to