You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by sm...@apache.org on 2022/04/22 12:48:50 UTC

[knox] branch master updated: KNOX-2734 - Passcode token is optional in TokenResource's response (#562)

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

smolnar 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 0b6bf4f85 KNOX-2734 - Passcode token is optional in TokenResource's response (#562)
0b6bf4f85 is described below

commit 0b6bf4f852aa9c0f5806d9ba4f3bcb6a62798900
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Fri Apr 22 14:48:46 2022 +0200

    KNOX-2734 - Passcode token is optional in TokenResource's response (#562)
---
 .../applications/tokengen/app/js/tokengen.js       |  7 ++-
 .../impl/AbstractPersistentTokenStateService.java  | 22 +++++++
 .../token/impl/AliasBasedTokenStateService.java    |  2 +-
 .../services/token/impl/JDBCTokenStateService.java |  2 +-
 .../token/impl/JournalBasedTokenStateService.java  |  2 +-
 .../impl/AliasBasedTokenStateServiceTest.java      | 69 +++++++++++-----------
 .../impl/JournalBasedTokenStateServiceTest.java    |  4 +-
 .../gateway/service/knoxtoken/TokenResource.java   |  8 ++-
 .../knoxtoken/TokenServiceResourceTest.java        | 50 ++++++++++++++++
 .../token/PersistentTokenStateService.java         | 24 ++++++++
 10 files changed, 148 insertions(+), 42 deletions(-)

diff --git a/gateway-applications/src/main/resources/applications/tokengen/app/js/tokengen.js b/gateway-applications/src/main/resources/applications/tokengen/app/js/tokengen.js
index 6aed159b9..04079e8eb 100644
--- a/gateway-applications/src/main/resources/applications/tokengen/app/js/tokengen.js
+++ b/gateway-applications/src/main/resources/applications/tokengen/app/js/tokengen.js
@@ -228,7 +228,12 @@ var gen = function() {
                     $('#accessToken').text(accessToken);
                     var decodedToken = b64DecodeUnicode(accessToken.split(".")[1]);
                     var jwtjson = JSON.parse(decodedToken);
-                    $('#accessPasscode').text(resp.passcode);
+                    if (resp.passcode && resp.passcode != "") {
+                        document.getElementById('jwtPasscodeTokenLabel').style.display = 'block';
+                        $('#accessPasscode').text(resp.passcode);
+                    } else {
+                        document.getElementById('jwtPasscodeTokenLabel').style.display = 'none';
+                    }
                     $('#expiry').text(new Date(resp.expires_in).toLocaleString());
                     $('#user').text(jwtjson.sub);
                     var homepageURL = resp.homepage_url;
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AbstractPersistentTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AbstractPersistentTokenStateService.java
new file mode 100644
index 000000000..f9566deca
--- /dev/null
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AbstractPersistentTokenStateService.java
@@ -0,0 +1,22 @@
+/*
+ * 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.services.token.impl;
+
+import org.apache.knox.gateway.services.security.token.PersistentTokenStateService;
+
+public abstract class AbstractPersistentTokenStateService extends DefaultTokenStateService implements PersistentTokenStateService {
+}
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 30895198c..a05dff08a 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
@@ -53,7 +53,7 @@ import org.apache.knox.gateway.util.Tokens;
 /**
  * A TokenStateService implementation based on the AliasService.
  */
-public class AliasBasedTokenStateService extends DefaultTokenStateService implements TokenStatePeristerMonitorListener {
+public class AliasBasedTokenStateService extends AbstractPersistentTokenStateService implements TokenStatePeristerMonitorListener {
 
   static final String TOKEN_ALIAS_SUFFIX_DELIM   = "--";
   static final String TOKEN_ISSUE_TIME_POSTFIX   = TOKEN_ALIAS_SUFFIX_DELIM + "iss";
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
index 6b27ec5df..ab8a5b4d7 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
@@ -39,7 +39,7 @@ import org.apache.knox.gateway.services.security.token.UnknownTokenException;
 import org.apache.knox.gateway.util.JDBCUtils;
 import org.apache.knox.gateway.util.Tokens;
 
-public class JDBCTokenStateService extends DefaultTokenStateService {
+public class JDBCTokenStateService extends AbstractPersistentTokenStateService {
   private AliasService aliasService; // connection username/pw and passcode HMAC secret are stored here
   private TokenStateDatabase tokenDatabase;
   private AtomicBoolean initialized = new AtomicBoolean(false);
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateService.java
index ee21c6a44..6487bd1dd 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateService.java
@@ -32,7 +32,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-public class JournalBasedTokenStateService extends DefaultTokenStateService {
+public class JournalBasedTokenStateService extends AbstractPersistentTokenStateService {
 
     private TokenStateJournal journal;
 
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 96e741244..5c6dbcba2 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
@@ -162,10 +162,10 @@ public class AliasBasedTokenStateServiceTest extends DefaultTokenStateServiceTes
     tss.setAliasService(aliasService);
     initTokenStateService(tss);
 
-    Map<String, Long> tokenExpirations = getTokenExpirationsField(tss, false);
-    Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss, false);
-    Map<String, Map<String, TokenMetadata>> metadata = getMetadataMapField(tss, false);
-    Map<String, Long> tokenIssueTimes = getTokenIssueTimesField(tss, false);
+    Map<String, Long> tokenExpirations = getTokenExpirationsField(tss, 2);
+    Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss, 2);
+    Map<String, Map<String, TokenMetadata>> metadata = getMetadataMapField(tss, 2);
+    Map<String, Long> tokenIssueTimes = getTokenIssueTimesField(tss, 2);
 
     final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
     final long maxTokenLifetime = evictionInterval * 3;
@@ -537,7 +537,7 @@ public class AliasBasedTokenStateServiceTest extends DefaultTokenStateServiceTes
 
     Map<String, Long> tokenExpirations = getTokenExpirationsField(tss);
     Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
-    Map<String, Long> tokenIssueTimes = getTokenIssueTimesField(tss, true);
+    Map<String, Long> tokenIssueTimes = getTokenIssueTimesField(tss, 3);
 
     Set<AliasBasedTokenStateService.TokenState> unpersistedState = getUnpersistedStateField(tss);
 
@@ -623,7 +623,7 @@ public class AliasBasedTokenStateServiceTest extends DefaultTokenStateServiceTes
 
     Map<String, Long> tokenExpirations = getTokenExpirationsField(tss);
     Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
-    Map<String, Long> tokenIssueTimes = getTokenIssueTimesField(tss, true);
+    Map<String, Long> tokenIssueTimes = getTokenIssueTimesField(tss, 3);
 
     Set<AliasBasedTokenStateService.TokenState> unpersistedState = getUnpersistedStateField(tss);
 
@@ -663,7 +663,7 @@ public class AliasBasedTokenStateServiceTest extends DefaultTokenStateServiceTes
       tss.renewToken(token);
     }
 
-    final List<AliasBasedTokenStateService.TokenState> unpersistedTokenStates = new ArrayList<>(getUnpersistedStateField(tss, false));
+    final List<AliasBasedTokenStateService.TokenState> unpersistedTokenStates = new ArrayList<>(getUnpersistedStateField(tss, 0));
     final int expectedAliasCount = 3 * tokenCount; //expiration + max + issue time for each token
     assertEquals(expectedAliasCount, unpersistedTokenStates.size());
     for (JWTToken token : testTokens) {
@@ -838,48 +838,49 @@ public class AliasBasedTokenStateServiceTest extends DefaultTokenStateServiceTes
   }
 
   private static Map<String, Long> getTokenExpirationsField(TokenStateService tss) throws Exception {
-    return getTokenExpirationsField(tss, true);
+    return getTokenExpirationsField(tss, 3);
   }
-  private static Map<String, Long> getTokenExpirationsField(TokenStateService tss, boolean fromGrandParent) throws Exception {
-    final Class<TokenStateService> clazz = (Class<TokenStateService>) (fromGrandParent ? tss.getClass().getSuperclass().getSuperclass() : tss.getClass().getSuperclass());
-    final Field tokenExpirationsField = clazz.getDeclaredField("tokenExpirations");
-    tokenExpirationsField.setAccessible(true);
-    return (Map<String, Long>) tokenExpirationsField.get(tss);
+
+  private static Map<String, Long> getTokenExpirationsField(TokenStateService tss, int level) throws Exception {
+    return (Map<String, Long>) getField(tss, level, "tokenExpirations");
+  }
+
+  private static Object getField(TokenStateService tss, int level, String fieldName) throws Exception {
+    final Field field = getParentClass(tss, level).getDeclaredField(fieldName);
+    field.setAccessible(true);
+    return field.get(tss);
+  }
+
+  private static Class<TokenStateService> getParentClass(TokenStateService tss, int level) {
+    Class<TokenStateService> clazz = (Class<TokenStateService>) tss.getClass();
+    for (int i = 1; i <= level; i++) {
+      clazz = (Class<TokenStateService>) clazz.getSuperclass();
+    }
+    return clazz;
   }
 
   private static Map<String, Long> getMaxTokenLifetimesField(TokenStateService tss) throws Exception {
-    return getMaxTokenLifetimesField(tss, true);
+    return getMaxTokenLifetimesField(tss, 3);
   }
 
-  private static Map<String, Long> getMaxTokenLifetimesField(TokenStateService tss, boolean fromGrandParent) throws Exception {
-    final Class<TokenStateService> clazz = (Class<TokenStateService>) (fromGrandParent ? tss.getClass().getSuperclass().getSuperclass() : tss.getClass().getSuperclass());
-    Field maxTokenLifetimesField = clazz.getDeclaredField("maxTokenLifetimes");
-    maxTokenLifetimesField.setAccessible(true);
-    return (Map<String, Long>) maxTokenLifetimesField.get(tss);
+  private static Map<String, Long> getMaxTokenLifetimesField(TokenStateService tss, int level) throws Exception {
+    return (Map<String, Long>) getField(tss, level, "maxTokenLifetimes");
   }
 
-  private static Map<String, Long> getTokenIssueTimesField(TokenStateService tss, boolean fromGrandParent) throws Exception {
-    final Class<TokenStateService> clazz = (Class<TokenStateService>) (fromGrandParent ? tss.getClass().getSuperclass().getSuperclass() : tss.getClass().getSuperclass());
-    Field tokenIssueTimesField = clazz.getDeclaredField("tokenIssueTimes");
-    tokenIssueTimesField.setAccessible(true);
-    return (Map<String, Long>) tokenIssueTimesField.get(tss);
+  private static Map<String, Long> getTokenIssueTimesField(TokenStateService tss, int level) throws Exception {
+    return (Map<String, Long>) getField(tss, level, "tokenIssueTimes");
   }
 
-  private static Map<String, Map<String, TokenMetadata>> getMetadataMapField(TokenStateService tss, boolean fromGrandParent) throws Exception {
-    final Class<TokenStateService> clazz = (Class<TokenStateService>) (fromGrandParent ? tss.getClass().getSuperclass().getSuperclass() : tss.getClass().getSuperclass());
-    Field metadataMapField = clazz.getDeclaredField("metadataMap");
-    metadataMapField.setAccessible(true);
-    return (Map<String, Map<String, TokenMetadata>>) metadataMapField.get(tss);
+  private static Map<String, Map<String, TokenMetadata>> getMetadataMapField(TokenStateService tss, int level) throws Exception {
+    return (Map<String, Map<String, TokenMetadata>>) getField(tss, level, "metadataMap");
   }
 
   private static Set<AliasBasedTokenStateService.TokenState> getUnpersistedStateField(TokenStateService tss) throws Exception {
-    return getUnpersistedStateField(tss, true);
+    return getUnpersistedStateField(tss, 1);
   }
 
-  private static Set<AliasBasedTokenStateService.TokenState> getUnpersistedStateField(TokenStateService tss, boolean fromParent) throws Exception {
-    Field unpersistedStateField = fromParent ? tss.getClass().getSuperclass().getDeclaredField("unpersistedState") : tss.getClass().getDeclaredField("unpersistedState");
-    unpersistedStateField.setAccessible(true);
-    return (Set<AliasBasedTokenStateService.TokenState>) unpersistedStateField.get(tss);
+  private static Set<AliasBasedTokenStateService.TokenState> getUnpersistedStateField(TokenStateService tss, int level) throws Exception {
+    return (Set<AliasBasedTokenStateService.TokenState>) getField(tss, level, "unpersistedState");
   }
 
   private static class TestJournalEntry implements JournalEntry {
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateServiceTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateServiceTest.java
index bb5c99f5d..82ecbe8ef 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateServiceTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateServiceTest.java
@@ -318,13 +318,13 @@ public class JournalBasedTokenStateServiceTest extends DefaultTokenStateServiceT
     }
 
     private static Map<String, Long> getTokenExpirationsField(TokenStateService tss) throws Exception {
-        Field tokenExpirationsField = tss.getClass().getSuperclass().getDeclaredField("tokenExpirations");
+        Field tokenExpirationsField = tss.getClass().getSuperclass().getSuperclass().getDeclaredField("tokenExpirations");
         tokenExpirationsField.setAccessible(true);
         return (Map<String, Long>) tokenExpirationsField.get(tss);
     }
 
     private static Map<String, Long> getMaxTokenLifetimesField(TokenStateService tss) throws Exception {
-        Field maxTokenLifetimesField = tss.getClass().getSuperclass().getDeclaredField("maxTokenLifetimes");
+        Field maxTokenLifetimesField = tss.getClass().getSuperclass().getSuperclass().getDeclaredField("maxTokenLifetimes");
         maxTokenLifetimesField.setAccessible(true);
         return (Map<String, Long>) maxTokenLifetimesField.get(tss);
     }
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
index 375e12e8c..85c07d33a 100644
--- a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
@@ -79,6 +79,7 @@ import org.apache.knox.gateway.services.security.token.JWTokenAttributes;
 import org.apache.knox.gateway.services.security.token.JWTokenAttributesBuilder;
 import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 import org.apache.knox.gateway.services.security.token.KnoxToken;
+import org.apache.knox.gateway.services.security.token.PersistentTokenStateService;
 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;
@@ -103,7 +104,7 @@ public class TokenResource {
   private static final String TOKEN_TYPE = "token_type";
   private static final String ACCESS_TOKEN = "access_token";
   private static final String TOKEN_ID = "token_id";
-  private static final String PASSCODE = "passcode";
+  static final String PASSCODE = "passcode";
   private static final String MANAGED_TOKEN = "managed";
   private static final String TARGET_URL = "target_url";
   private static final String ENDPOINT_PUBLIC_CERT = "endpoint_public_cert";
@@ -804,8 +805,11 @@ public class TokenResource {
         if (endpointPublicCert != null) {
           map.put(ENDPOINT_PUBLIC_CERT, endpointPublicCert);
         }
+
         final String passcode = UUID.randomUUID().toString();
-        map.put(PASSCODE, generatePasscodeField(tokenId, passcode));
+        if (tokenStateService != null && tokenStateService instanceof PersistentTokenStateService) {
+          map.put(PASSCODE, generatePasscodeField(tokenId, passcode));
+        }
 
         String jsonResponse = JsonUtils.renderAsJsonString(map);
 
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 49b8772ae..6de3f2a2d 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
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -87,6 +88,7 @@ import org.apache.knox.gateway.services.security.AliasService;
 import org.apache.knox.gateway.services.security.token.JWTokenAttributes;
 import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 import org.apache.knox.gateway.services.security.token.KnoxToken;
+import org.apache.knox.gateway.services.security.token.PersistentTokenStateService;
 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;
@@ -1193,6 +1195,48 @@ public class TokenServiceResourceTest {
     assertEquals(400, response.getStatus());
   }
 
+  @Test
+  public void passcodeShouldNotBeInResponseIfTokenStateServiceIsDisabled() throws Exception {
+    testPasscodeToken(false, false, false);
+  }
+
+  @Test
+  public void passcodeShouldNotBeInResponseIfTokenStateServiceIsNotPersistent() throws Exception {
+    testPasscodeToken(true, false, false);
+  }
+
+  @Test
+  public void passcodeShouldBeInResponseIfTokenStateServiceIsEnabledAndPersistent() throws Exception {
+    testPasscodeToken(true, true, true);
+  }
+
+  private void testPasscodeToken(boolean serverManagedTssEnabled, boolean usePersistentTokenStore, boolean expectPasscodeInResponse) throws Exception {
+    try {
+      if (usePersistentTokenStore) {
+        tss = new PersistentTestTokenStateService();
+      }
+      configureCommonExpectations(new HashMap<>(), serverManagedTssEnabled);
+
+      final TokenResource tr = new TokenResource();
+      tr.context = context;
+      tr.request = request;
+      tr.init();
+
+      // Issue a token
+      final Response response = tr.doGet();
+      assertEquals(200, response.getStatus());
+      final String retString = response.getEntity().toString();
+      final String passcode = getTagValue(retString, TokenResource.PASSCODE);
+      if (expectPasscodeInResponse) {
+        assertNotNull(passcode);
+      } else {
+        assertNull(passcode);
+      }
+    } finally {
+      tss = new TestTokenStateService();
+    }
+  }
+
   /**
    *
    * @param isTokenStateServerManaged true, if server-side token state management should be enabled; Otherwise, false or null.
@@ -1442,6 +1486,9 @@ public class TokenServiceResourceTest {
 
 
   private String getTagValue(String token, String tagName) {
+    if (!token.contains(tagName)) {
+      return null;
+    }
     String searchString = tagName + "\":";
     String value = token.substring(token.indexOf(searchString) + searchString.length());
     if (value.startsWith("\"")) {
@@ -1629,6 +1676,9 @@ public class TokenServiceResourceTest {
     }
   }
 
+  private static class PersistentTestTokenStateService extends TestTokenStateService implements PersistentTokenStateService {
+  }
+
   private static class TestJWTokenAuthority implements JWTokenAuthority {
 
     private RSAPublicKey publicKey;
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/PersistentTokenStateService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/PersistentTokenStateService.java
new file mode 100644
index 000000000..e88176163
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/PersistentTokenStateService.java
@@ -0,0 +1,24 @@
+/*
+ * 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.services.security.token;
+
+/**
+ * A marker interface that indicates the tokens are stored in a persistent
+ * repository (FileSystem, Database, ZooKeeper, etc...)
+ */
+public interface PersistentTokenStateService extends TokenStateService {
+}