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;
+ }
+ }
}