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 f91385662 KNOX-2961 - Knox SSO cookie Invalidation - Phase II (#799)
f91385662 is described below

commit f91385662a09b1014f1e1935944fb55bcb47f0a0
Author: Sandor Molnar <[email protected]>
AuthorDate: Mon Oct 9 13:29:10 2023 +0200

    KNOX-2961 - Knox SSO cookie Invalidation - Phase II (#799)
    
    - Allow end-users to show/hide previously disabled KnoxSSO Cookies on the 
Token Management page.
    - Pre-configured users can see all tokens on the Token Management page.
    - End-users can execute batch operations on selected Knox Tokens.
---
 ...okenIDAsHTTPBasicCredsFederationFilterTest.java |  15 ++-
 gateway-release/home/conf/gateway-site.xml         |   7 +
 .../gateway/config/impl/GatewayConfigImpl.java     |   7 +
 .../token/impl/DefaultTokenStateService.java       |  15 ++-
 .../services/token/impl/JDBCTokenStateService.java |  10 ++
 .../services/token/impl/TokenStateDatabase.java    |  16 ++-
 .../token/impl/TokenStateServiceMessages.java      |   3 +
 .../token/impl/JDBCTokenStateServiceTest.java      |  27 +++-
 gateway-service-knoxtoken/pom.xml                  |   5 +
 .../gateway/service/knoxtoken/TokenResource.java   |  78 +++++++++--
 .../service/knoxtoken/TokenServiceMessages.java    |   3 +
 .../knoxtoken/TokenServiceResourceTest.java        |  15 ++-
 .../service/session/SessionInformation.java        |  11 ++
 .../gateway/service/session/SessionResource.java   |   4 +-
 .../org/apache/knox/gateway/GatewayTestConfig.java |   5 +
 .../apache/knox/gateway/config/GatewayConfig.java  |   7 +
 .../services/security/token/TokenStateService.java |   5 +
 .../token-management/app/app.module.ts             |   6 +-
 .../token-management/app/knox.token.ts             |   4 +
 .../app/{knox.token.ts => session.information.ts}  |  16 +--
 .../app/token.management.component.html            |  49 ++++++-
 .../app/token.management.component.ts              | 144 +++++++++++++++++++--
 .../app/token.management.service.ts                |  52 +++++++-
 23 files changed, 445 insertions(+), 59 deletions(-)

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 7a1525527..65a48cf53 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
@@ -486,6 +486,11 @@ public class TokenIDAsHTTPBasicCredsFederationFilterTest 
extends JWTAsHTTPBasicC
             return tokenMetadata.get(tokenId);
         }
 
+        @Override
+        public Collection<KnoxToken> getAllTokens() {
+           return fetchTokens(null, false);
+        }
+
         @Override
         public Collection<KnoxToken> getTokens(String userName) {
           return fetchTokens(userName, false);
@@ -499,10 +504,14 @@ public class TokenIDAsHTTPBasicCredsFederationFilterTest 
extends JWTAsHTTPBasicC
         private Collection<KnoxToken> fetchTokens(String userName, boolean 
createdBy) {
           final Collection<KnoxToken> tokens = new TreeSet<>();
           final Predicate<Map.Entry<String, TokenMetadata>> filterPredicate;
-          if (createdBy) {
-            filterPredicate = entry -> 
userName.equals(entry.getValue().getCreatedBy());
+          if (userName == null) {
+            filterPredicate = entry -> true;
           } else {
-            filterPredicate = entry -> 
userName.equals(entry.getValue().getUserName());
+            if (createdBy) {
+              filterPredicate = entry -> 
userName.equals(entry.getValue().getCreatedBy());
+            } else {
+              filterPredicate = entry -> 
userName.equals(entry.getValue().getUserName());
+            }
           }
           
tokenMetadata.entrySet().stream().filter(filterPredicate).forEach(metadata -> {
             String tokenId = metadata.getKey();
diff --git a/gateway-release/home/conf/gateway-site.xml 
b/gateway-release/home/conf/gateway-site.xml
index aa7a675dc..7a02fa58e 100644
--- a/gateway-release/home/conf/gateway-site.xml
+++ b/gateway-release/home/conf/gateway-site.xml
@@ -127,6 +127,13 @@ limitations under the License.
         <description>Enable/disable logout from the Knox 
Homepage.</description>
     </property>
 
+    <!-- @since 2.1.0 KnoxSSO Cookie Invalidation -->
+    <property>
+        <name>gateway.knox.token.management.users.can.see.all.tokens</name>
+        <value>admin</value>
+        <description>A comma separated list of user names who can see all 
tokens on the Token Management page</description>
+    </property>
+
     <!-- @since 1.6.0 token management related properties -->
     <property>
         <name>gateway.knox.token.eviction.grace.period</name>
diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
index 5663aaf34..e065d548e 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
@@ -281,6 +281,7 @@ public class GatewayConfigImpl extends Configuration 
implements GatewayConfig {
   public static final String X_FORWARD_CONTEXT_HEADER_APPEND_SERVICES = 
GATEWAY_CONFIG_FILE_PREFIX + ".xforwarded.header.context.append.servicename";
 
   private static final String TOKEN_STATE_SERVER_MANAGED = 
GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.exp.server-managed";
+  private static final String USERS_CAN_SEE_ALL_TOKENS = 
GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.management.users.can.see.all.tokens";
 
   private static final String CLOUDERA_MANAGER_DESCRIPTORS_MONITOR_INTERVAL = 
GATEWAY_CONFIG_FILE_PREFIX + ".cloudera.manager.descriptors.monitor.interval";
   private static final String 
CLOUDERA_MANAGER_ADVANCED_SERVICE_DISCOVERY_CONF_MONITOR_INTERVAL = 
GATEWAY_CONFIG_FILE_PREFIX + 
".cloudera.manager.advanced.service.discovery.config.monitor.interval";
@@ -1489,4 +1490,10 @@ public class GatewayConfigImpl extends Configuration 
implements GatewayConfig {
     return getBoolean(GATEWAY_SERVLET_ASYNC_SUPPORTED, 
GATEWAY_SERVLET_ASYNC_SUPPORTED_DEFAULT);
   }
 
+  @Override
+  public boolean canSeeAllTokens(String userName) {
+    final Collection<String> usersCanSeeAllTokens = 
getTrimmedStringCollection(USERS_CAN_SEE_ALL_TOKENS);
+    return usersCanSeeAllTokens == null ? false : 
usersCanSeeAllTokens.contains(userName);
+  }
+
 }
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 9eb327e8f..cbb783b38 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
@@ -419,6 +419,11 @@ public class DefaultTokenStateService implements 
TokenStateService {
     return metadataMap.get(tokenId);
   }
 
+  @Override
+  public Collection<KnoxToken> getAllTokens() {
+    return fetchTokens(null, false);
+  }
+
   @Override
   public Collection<KnoxToken> getTokens(String userName) {
     return fetchTokens(userName, false);
@@ -432,10 +437,14 @@ public class DefaultTokenStateService implements 
TokenStateService {
   private Collection<KnoxToken> fetchTokens(String userName, boolean 
createdBy) {
     final Collection<KnoxToken> tokens = new TreeSet<>();
     final Predicate<Map.Entry<String, TokenMetadata>> filterPredicate;
-    if (createdBy) {
-      filterPredicate = entry -> 
userName.equals(entry.getValue().getCreatedBy());
+    if (userName == null) {
+      filterPredicate = entry -> true;
     } else {
-      filterPredicate = entry -> 
userName.equals(entry.getValue().getUserName());
+      if (createdBy) {
+        filterPredicate = entry -> 
userName.equals(entry.getValue().getCreatedBy());
+      } else {
+        filterPredicate = entry -> 
userName.equals(entry.getValue().getUserName());
+      }
     }
     metadataMap.entrySet().stream().filter(filterPredicate).forEach(metadata 
-> {
       String tokenId = metadata.getKey();
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 ab8a5b4d7..7110325af 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
@@ -294,6 +294,16 @@ public class JDBCTokenStateService extends 
AbstractPersistentTokenStateService {
     return tokenMetadata;
   }
 
+  @Override
+  public Collection<KnoxToken> getAllTokens() {
+    try {
+      return tokenDatabase.getAllTokens();
+    } catch (SQLException e) {
+      log.errorFetchingAllTokensFromDatabase(e.getMessage(), e);
+      return Collections.emptyList();
+    }
+  }
+
   @Override
   public Collection<KnoxToken> getTokens(String userName) {
     try {
diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
index b5f5f0abd..a461171b4 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
@@ -52,11 +52,11 @@ public class TokenStateDatabase {
   private static final String ADD_METADATA_SQL = "INSERT INTO " + 
TOKEN_METADATA_TABLE_NAME + "(token_id, md_name, md_value) VALUES(?, ?, ?)";
   private static final String UPDATE_METADATA_SQL = "UPDATE " + 
TOKEN_METADATA_TABLE_NAME + " SET md_value = ? WHERE token_id = ? AND md_name = 
?";
   private static final String GET_METADATA_SQL = "SELECT md_name, md_value 
FROM " + TOKEN_METADATA_TABLE_NAME + " WHERE token_id = ?";
-  private static final String GET_TOKENS_BY_USER_NAME_SQL = "SELECT 
kt.token_id, kt.issue_time, kt.expiration, kt.max_lifetime, ktm.md_name, 
ktm.md_value FROM " + TOKENS_TABLE_NAME
-      + " kt, " + TOKEN_METADATA_TABLE_NAME + " ktm WHERE kt.token_id = 
ktm.token_id AND kt.token_id IN (SELECT token_id FROM " + 
TOKEN_METADATA_TABLE_NAME + " WHERE md_name = '" + TokenMetadata.USER_NAME + "' 
AND md_value = ? )"
+  private static final String GET_ALL_TOKENS_SQL = "SELECT kt.token_id, 
kt.issue_time, kt.expiration, kt.max_lifetime, ktm.md_name, ktm.md_value FROM " 
+ TOKENS_TABLE_NAME
+      + " kt, " + TOKEN_METADATA_TABLE_NAME + " ktm WHERE kt.token_id = 
ktm.token_id";
+  private static final String GET_TOKENS_BY_USER_NAME_SQL = GET_ALL_TOKENS_SQL 
+ " AND kt.token_id IN (SELECT token_id FROM " + TOKEN_METADATA_TABLE_NAME + " 
WHERE md_name = '" + TokenMetadata.USER_NAME + "' AND md_value = ? )"
       + " ORDER BY kt.issue_time";
-  private static final String GET_TOKENS_CREATED_BY_USER_NAME_SQL = "SELECT 
kt.token_id, kt.issue_time, kt.expiration, kt.max_lifetime, ktm.md_name, 
ktm.md_value FROM " + TOKENS_TABLE_NAME
-      + " kt, " + TOKEN_METADATA_TABLE_NAME + " ktm WHERE kt.token_id = 
ktm.token_id AND kt.token_id IN (SELECT token_id FROM " + 
TOKEN_METADATA_TABLE_NAME + " WHERE md_name = '" + TokenMetadata.CREATED_BY + 
"' AND md_value = ? )"
+  private static final String GET_TOKENS_CREATED_BY_USER_NAME_SQL = 
GET_ALL_TOKENS_SQL + " AND kt.token_id IN (SELECT token_id FROM " + 
TOKEN_METADATA_TABLE_NAME + " WHERE md_name = '" + TokenMetadata.CREATED_BY + 
"' AND md_value = ? )"
       + " ORDER BY kt.issue_time";
 
   private final DataSource dataSource;
@@ -181,6 +181,10 @@ public class TokenStateDatabase {
     return metadataName.equals(TokenMetadata.PASSCODE) ? new 
String(Base64.decodeBase64(metadataValue.getBytes(UTF_8)), UTF_8) : 
metadataValue;
   }
 
+  Collection<KnoxToken> getAllTokens() throws SQLException {
+    return fetchTokens(null, GET_ALL_TOKENS_SQL);
+  }
+
   Collection<KnoxToken> getTokens(String userName) throws SQLException {
     return fetchTokens(userName, GET_TOKENS_BY_USER_NAME_SQL);
   }
@@ -192,7 +196,9 @@ public class TokenStateDatabase {
   private Collection<KnoxToken> fetchTokens(String userName, String sql) 
throws SQLException {
     Map<String, KnoxToken> tokenMap = new LinkedHashMap<>();
     try (Connection connection = dataSource.getConnection(); PreparedStatement 
getTokenIdsStatement = connection.prepareStatement(sql)) {
-      getTokenIdsStatement.setString(1, userName);
+      if (userName != null) {
+        getTokenIdsStatement.setString(1, userName);
+      }
       try (ResultSet rs = getTokenIdsStatement.executeQuery()) {
         while (rs.next()) {
           String tokenId = rs.getString(1);
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 75cbb1b27..166e89ae7 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
@@ -253,6 +253,9 @@ public interface TokenStateServiceMessages {
   @Message(level = MessageLevel.ERROR, text = "An error occurred while 
fetching metadata for {0} from the database : {1}")
   void errorFetchingMetadataFromDatabase(String tokenId, String errorMessage, 
@StackTrace(level = MessageLevel.DEBUG) Exception e);
 
+  @Message(level = MessageLevel.ERROR, text = "An error occurred while 
fetching all tokens from the database : {0}")
+  void errorFetchingAllTokensFromDatabase(String errorMessage, 
@StackTrace(level = MessageLevel.DEBUG) Exception e);
+
   @Message(level = MessageLevel.ERROR, text = "An error occurred while 
fetching tokens for user {0} from the database : {1}")
   void errorFetchingTokensForUserFromDatabase(String userName, String 
errorMessage, @StackTrace(level = MessageLevel.DEBUG) Exception e);
 
diff --git 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateServiceTest.java
 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateServiceTest.java
index 024025282..205c59191 100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateServiceTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateServiceTest.java
@@ -118,6 +118,7 @@ public class JDBCTokenStateServiceTest {
     String id1 = "token1";
     String id2 = "token2";
     String id3 = "token3";
+    String createdBy3 = "createdBy3";
 
     long issueTime1 = 1;
     long expiration1 = 1;
@@ -144,15 +145,39 @@ public class JDBCTokenStateServiceTest {
 
     List<KnoxToken> user2Tokens = new 
ArrayList<>(jdbcTokenStateService.getTokens(user2));
     assertEquals(1, user2Tokens.size());
-    assertToken(user2Tokens.get(0), id3, expiration3, comment3, issueTime3);
+    KnoxToken token3 = user2Tokens.get(0);
+    assertToken(token3, id3, expiration3, comment3, issueTime3);
+
+    // check doAs tokens
+    TokenMetadata token3Metadata = new 
TokenMetadata(token3.getMetadata().getUserName());
+    token3Metadata.add(TokenMetadata.CREATED_BY, createdBy3);
+    jdbcTokenStateService.addMetadata(id3, token3Metadata);
+    List<KnoxToken> createdBy3Tokens = new 
ArrayList<>(jdbcTokenStateService.getDoAsTokens(createdBy3));
+    assertEquals(1, createdBy3Tokens.size());
+    KnoxToken createdBy3Token = createdBy3Tokens.get(0);
+    assertToken(createdBy3Token, id3, expiration3, comment3, issueTime3, 
createdBy3);
+
+    // check all tokens
+    List<KnoxToken> allTokens = new 
ArrayList<>(jdbcTokenStateService.getAllTokens());
+    assertEquals(3, allTokens.size());
+    assertToken(allTokens.get(0), id1, expiration1, comment1, issueTime1);
+    assertToken(allTokens.get(1), id2, expiration2, comment2, issueTime2);
+    assertToken(allTokens.get(2), id3, expiration3, comment3, issueTime3, 
createdBy3);
   }
 
   private void assertToken(KnoxToken knoxToken, String tokenId, long 
expiration, String comment, long issueTime) {
+    assertToken(knoxToken, tokenId, expiration, comment, issueTime, null);
+  }
+
+  private void assertToken(KnoxToken knoxToken, String tokenId, long 
expiration, String comment, long issueTime, String createdBy) {
     SimpleDateFormat df = new SimpleDateFormat(KnoxToken.DATE_FORMAT, 
Locale.getDefault());
     assertEquals(tokenId, knoxToken.getTokenId());
     assertEquals(df.format(new Date(issueTime)), knoxToken.getIssueTime());
     assertEquals(df.format(new Date(expiration)), knoxToken.getExpiration());
     assertEquals(comment, knoxToken.getMetadata().getComment());
+    if (createdBy != null) {
+      assertEquals(createdBy, knoxToken.getMetadata().getCreatedBy());
+    }
   }
 
   private void saveToken(String user, String tokenId, long issueTime, long 
expiration, String comment) {
diff --git a/gateway-service-knoxtoken/pom.xml 
b/gateway-service-knoxtoken/pom.xml
index 305c86aca..e5422f45a 100644
--- a/gateway-service-knoxtoken/pom.xml
+++ b/gateway-service-knoxtoken/pom.xml
@@ -101,6 +101,11 @@
             </exclusions>
         </dependency>
 
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.knox</groupId>
             <artifactId>gateway-test-utils</artifactId>
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 3784e8b10..33300993a 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
@@ -46,6 +46,7 @@ import javax.inject.Singleton;
 import javax.security.auth.Subject;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
@@ -56,6 +57,7 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
+import com.google.gson.Gson;
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.KeyLengthException;
 import com.nimbusds.jose.crypto.MACSigner;
@@ -138,14 +140,18 @@ public class TokenResource {
   static final String GET_TSS_STATUS_PATH = "/getTssStatus";
   static final String RENEW_PATH = "/renew";
   static final String REVOKE_PATH = "/revoke";
+  static final String BATCH_REVOKE_PATH = "/revokeTokens";
   static final String ENABLE_PATH = "/enable";
+  static final String BATCH_ENABLE_PATH = "/enableTokens";
   static final String DISABLE_PATH = "/disable";
+  static final String BATCH_DISABLE_PATH = "/disableTokens";
   private static final String TARGET_ENDPOINT_PULIC_CERT_PEM = 
TOKEN_PARAM_PREFIX + "target.endpoint.cert.pem";
   static final String QUERY_PARAMETER_DOAS = "doAs";
   private static final String IMPERSONATION_ENABLED_TEXT = 
"impersonationEnabled";
   public static final String KNOX_TOKEN_INCLUDE_GROUPS = TOKEN_PARAM_PREFIX + 
"include.groups";
   public static final String KNOX_TOKEN_ISSUER = TOKEN_PARAM_PREFIX + "issuer";
   private static TokenServiceMessages log = 
MessagesFactory.get(TokenServiceMessages.class);
+  private static final Gson GSON = new Gson();
   private long tokenTTL = TOKEN_TTL_DEFAULT;
   private String tokenType;
   private String tokenTTLAsText;
@@ -189,7 +195,8 @@ public class TokenResource {
     INVALID_TOKEN(40),
     UNKNOWN_TOKEN(50),
     ALREADY_DISABLED(60),
-    ALREADY_ENABLED(70);
+    ALREADY_ENABLED(70),
+    DISABLED_KNOXSSO_COOKIE(80);
 
     private final int code;
 
@@ -452,8 +459,11 @@ public class TokenResource {
       final String userName = 
uriInfo.getQueryParameters().getFirst("userName");
       final String createdBy = 
uriInfo.getQueryParameters().getFirst("createdBy");
       final String userNameOrCreatedBy = 
uriInfo.getQueryParameters().getFirst("userNameOrCreatedBy");
+      final boolean allTokens = 
Boolean.parseBoolean(uriInfo.getQueryParameters().getFirst("allTokens"));
       final Collection<KnoxToken> userTokens;
-      if (userNameOrCreatedBy == null) {
+      if (allTokens) {
+        userTokens = tokenStateService.getAllTokens();
+      } else if (userNameOrCreatedBy == null) {
         userTokens = createdBy == null ? tokenStateService.getTokens(userName) 
: tokenStateService.getDoAsTokens(createdBy);
       } else {
         userTokens = new 
HashSet<>(tokenStateService.getTokens(userNameOrCreatedBy));
@@ -559,6 +569,22 @@ public class TokenResource {
     return resp;
   }
 
+  @DELETE
+  @Path(BATCH_REVOKE_PATH)
+  @Produces({APPLICATION_JSON})
+  public Response revokeTokens(String tokenIds) {
+    final List<String> ids = GSON.fromJson(tokenIds, List.class);
+    Response response = null;
+    Response error = null;
+    for (String tokenId : ids) {
+      response = revoke(tokenId);
+      if (response.getStatus() != Response.Status.OK.getStatusCode()) {
+        error = response;
+      }
+    }
+    return error == null ? response : error;
+  }
+
   @DELETE
   @Path(REVOKE_PATH)
   @Produces({APPLICATION_JSON})
@@ -580,8 +606,7 @@ public class TokenResource {
           errorStatus = Response.Status.FORBIDDEN;
           error = "SSO cookie (" + Tokens.getTokenIDDisplayText(tokenId) + ") 
cannot not be revoked." ;
           errorCode = ErrorCode.UNAUTHORIZED;
-        }
-        if (StringUtils.isBlank(error) && (triesToRevokeOwnToken(tokenId, 
revoker) || allowedRenewers.contains(revoker))) {
+        } else if (triesToRevokeOwnToken(tokenId, revoker) || 
allowedRenewers.contains(revoker)) {
           tokenStateService.revokeToken(tokenId);
           log.revokedToken(getTopologyName(),
               Tokens.getTokenDisplayText(token),
@@ -648,17 +673,47 @@ public class TokenResource {
   @Path(ENABLE_PATH)
   @Produces({ APPLICATION_JSON })
   public Response enable(String tokenId) {
-    return setTokenEnabledFlag(tokenId, true);
+    return setTokenEnabledFlag(tokenId, true, false);
+  }
+
+  @PUT
+  @Path(BATCH_ENABLE_PATH)
+  @Consumes({ APPLICATION_JSON })
+  @Produces({ APPLICATION_JSON })
+  public Response enableTokens(String tokenIds) {
+    return setTokenEnabledFlags(tokenIds, true);
   }
 
   @PUT
   @Path(DISABLE_PATH)
   @Produces({ APPLICATION_JSON })
   public Response disable(String tokenId) {
-    return setTokenEnabledFlag(tokenId, false);
+    return setTokenEnabledFlag(tokenId, false, false);
+  }
+
+  @PUT
+  @Path(BATCH_DISABLE_PATH)
+  @Consumes({ APPLICATION_JSON })
+  @Produces({ APPLICATION_JSON })
+  public Response disableTokens(String tokenIds) {
+    return setTokenEnabledFlags(tokenIds, false);
   }
 
-  private Response setTokenEnabledFlag(String tokenId, boolean enabled) {
+  @SuppressWarnings("unchecked")
+  private Response setTokenEnabledFlags(String tokenIds, boolean enabled) {
+    final List<String> ids = GSON.fromJson(tokenIds, List.class);
+    Response response = null;
+    Response error = null;
+    for (String tokenId : ids) {
+      response = setTokenEnabledFlag(tokenId, enabled, true);
+      if (response.getStatus() != Response.Status.OK.getStatusCode()) {
+        error = response;
+      }
+    }
+    return error == null ? response : error;
+  }
+
+  private Response setTokenEnabledFlag(String tokenId, boolean enabled, 
boolean batch) {
     String error = "";
     ErrorCode errorCode = ErrorCode.UNKNOWN;
     if (tokenStateService == null) {
@@ -667,12 +722,15 @@ public class TokenResource {
     } else {
       try {
         final TokenMetadata tokenMetadata = 
tokenStateService.getTokenMetadata(tokenId);
-        if (enabled && tokenMetadata.isEnabled()) {
+        if (!batch && enabled && tokenMetadata.isEnabled()) {
           error = "Token is already enabled";
           errorCode = ErrorCode.ALREADY_ENABLED;
-        } else if (!enabled && !tokenMetadata.isEnabled()) {
+        } else if (!batch && !enabled && !tokenMetadata.isEnabled()) {
           error = "Token is already disabled";
           errorCode = ErrorCode.ALREADY_DISABLED;
+        } else if (enabled && tokenMetadata.isKnoxSsoCookie()) {
+          error = "Disabled KnoxSSO Cookies cannot not be enabled";
+          errorCode = ErrorCode.DISABLED_KNOXSSO_COOKIE;
         } else {
           tokenMetadata.setEnabled(enabled);
           tokenStateService.addMetadata(tokenId, tokenMetadata);
@@ -682,7 +740,9 @@ public class TokenResource {
         errorCode = ErrorCode.UNKNOWN_TOKEN;
       }
     }
+
     if (error.isEmpty()) {
+      log.setEnabledFlag(getTopologyName(), enabled, 
Tokens.getTokenIDDisplayText(tokenId));
       return Response.status(Response.Status.OK).entity("{\n  
\"setEnabledFlag\": \"true\",\n  \"isEnabled\": \"" + enabled + 
"\"\n}\n").build();
     } else {
       log.badSetEnabledFlagRequest(getTopologyName(), 
Tokens.getTokenIDDisplayText(tokenId), error);
diff --git 
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
index 5d0254e35..87b647943 100644
--- 
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
+++ 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
@@ -36,6 +36,9 @@ public interface TokenServiceMessages {
   @Message( level = MessageLevel.INFO, text = "Knox Token service ({0}) 
revoked token {1} ({2}) (renewer={3})")
   void revokedToken(String topologyName, String tokenDisplayText, String 
tokenId, String renewer);
 
+  @Message( level = MessageLevel.INFO, text = "Knox Token service ({0}) set 
enabled flag to {1} on token {2}")
+  void setEnabledFlag(String topologyName, boolean enabled, String tokenId);
+
   @Message( level = MessageLevel.ERROR, text = "Unable to issue token.")
   void unableToIssueToken(@StackTrace( level = MessageLevel.DEBUG) Exception 
e);
 
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 5f8bb0e2e..44c6f58e2 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
@@ -1724,6 +1724,11 @@ public class TokenServiceResourceTest {
       return tokenMetadata.get(tokenId);
     }
 
+    @Override
+    public Collection<KnoxToken> getAllTokens() {
+      return fetchTokens(null, false);
+    }
+
     @Override
     public Collection<KnoxToken> getTokens(String userName) {
       return fetchTokens(userName, false);
@@ -1737,10 +1742,14 @@ public class TokenServiceResourceTest {
     private Collection<KnoxToken> fetchTokens(String userName, boolean 
createdBy) {
       final Collection<KnoxToken> tokens = new TreeSet<>();
       final Predicate<Map.Entry<String, TokenMetadata>> filterPredicate;
-      if (createdBy) {
-        filterPredicate = entry -> 
userName.equals(entry.getValue().getCreatedBy());
+      if (userName == null) {
+        filterPredicate = entry -> true;
       } else {
-        filterPredicate = entry -> 
userName.equals(entry.getValue().getUserName());
+        if (createdBy) {
+          filterPredicate = entry -> 
userName.equals(entry.getValue().getCreatedBy());
+        } else {
+          filterPredicate = entry -> 
userName.equals(entry.getValue().getUserName());
+        }
       }
       
tokenMetadata.entrySet().stream().filter(filterPredicate).forEach(metadata -> {
         String tokenId = metadata.getKey();
diff --git 
a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java
 
b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java
index e3822e0c7..9ebba4632 100644
--- 
a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java
+++ 
b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java
@@ -34,6 +34,9 @@ public class SessionInformation {
   @XmlElement
   private String globalLogoutPageUrl;
 
+  @XmlElement
+  private boolean canSeeAllTokens;
+
   public String getUser() {
     return user;
   }
@@ -66,4 +69,12 @@ public class SessionInformation {
     this.globalLogoutPageUrl = globalLogoutPageUrl;
   }
 
+  public boolean isCanSeeAllTokens() {
+    return canSeeAllTokens;
+  }
+
+  public void setCanSeeAllTokens(boolean canSeeAllTokens) {
+    this.canSeeAllTokens = canSeeAllTokens;
+  }
+
 }
diff --git 
a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java
 
b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java
index 4bffced27..1d1500294 100644
--- 
a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java
+++ 
b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java
@@ -48,7 +48,8 @@ public class SessionResource {
   @Path("sessioninfo")
   public SessionInformation getSessionInformation() {
     final SessionInformation sessionInfo = new SessionInformation();
-    sessionInfo.setUser(SubjectUtils.getCurrentEffectivePrincipalName());
+    final String user = SubjectUtils.getCurrentEffectivePrincipalName();
+    sessionInfo.setUser(user);
     final GatewayConfig config = (GatewayConfig) 
context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
     if (config != null && config.homePageLogoutEnabled()) {
       String logoutUrl = getBaseGatewayUrl(config) + 
"/homepage/knoxssout/api/v1/webssout";
@@ -56,6 +57,7 @@ public class SessionResource {
       sessionInfo.setLogoutUrl(logoutUrl);
       sessionInfo.setLogoutPageUrl(getLogoutPageUrl(config));
       sessionInfo.setGlobalLogoutPageUrl(getGlobalLogoutPageUrl(config));
+      sessionInfo.setCanSeeAllTokens(config.canSeeAllTokens(user));
     }
 
     return sessionInfo;
diff --git 
a/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
 
b/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
index f34a83cc4..3a3a61d29 100644
--- 
a/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
+++ 
b/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
@@ -1057,4 +1057,9 @@ public class GatewayTestConfig extends Configuration 
implements GatewayConfig {
     return false;
   }
 
+  @Override
+  public boolean canSeeAllTokens(String userName) {
+    return false;
+  }
+
 }
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java 
b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
index 7c37a96c4..b46958973 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
@@ -887,4 +887,11 @@ public interface GatewayConfig {
    */
   boolean isAsyncSupported();
 
+  /**
+   * @return <code>true</code> if the supplied user is allowed to see all 
tokens
+   *         (i.e. not only tokens where userName or createdBy equals to the
+   *         userName) on the Token Management page; <code>false</code> 
otherwise
+   */
+  boolean canSeeAllTokens(String userName);
+
 }
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 56df224cf..2d3ea1b20 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
@@ -195,6 +195,11 @@ public interface TokenStateService extends Service {
    */
   TokenMetadata getTokenMetadata(String tokenId) throws UnknownTokenException;
 
+  /**
+   * @return a collection of all the existing (not yet evicted) tokens
+   */
+  Collection<KnoxToken> getAllTokens();
+
   /**
    * @param userName The name of the user to get tokens for
    * @return a collection of tokens associated to the given user; it's an empty
diff --git a/knox-token-management-ui/token-management/app/app.module.ts 
b/knox-token-management-ui/token-management/app/app.module.ts
index 8ef4ab433..f49275980 100644
--- a/knox-token-management-ui/token-management/app/app.module.ts
+++ b/knox-token-management-ui/token-management/app/app.module.ts
@@ -24,6 +24,8 @@ import {MatSortModule} from '@angular/material/sort';
 import {MatPaginatorModule} from '@angular/material/paginator';
 import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
 import {MatInputModule} from '@angular/material/input';
+import {MatSlideToggleModule} from '@angular/material/slide-toggle';
+import {MatCheckboxModule} from '@angular/material/checkbox';
 import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
 import {FormsModule, ReactiveFormsModule} from '@angular/forms';
 
@@ -46,7 +48,9 @@ import {TokenManagementService} from 
'./token.management.service';
         MatSortModule,
         MatPaginatorModule,
         MatProgressSpinnerModule,
-        MatInputModule
+        MatInputModule,
+        MatSlideToggleModule,
+        MatCheckboxModule
     ],
     declarations: [TokenManagementComponent],
     providers: [TokenManagementService],
diff --git a/knox-token-management-ui/token-management/app/knox.token.ts 
b/knox-token-management-ui/token-management/app/knox.token.ts
index c21e979ef..f22428265 100644
--- a/knox-token-management-ui/token-management/app/knox.token.ts
+++ b/knox-token-management-ui/token-management/app/knox.token.ts
@@ -25,4 +25,8 @@ export class KnoxToken {
     expirationLong: number;
     maxLifetime: string;
     metadata: Metadata;
+
+    public toString = (): string => {
+        return 'KnoxToken (tokenId: ${this.tokenId})';
+    };
 }
diff --git a/knox-token-management-ui/token-management/app/knox.token.ts 
b/knox-token-management-ui/token-management/app/session.information.ts
similarity index 77%
copy from knox-token-management-ui/token-management/app/knox.token.ts
copy to knox-token-management-ui/token-management/app/session.information.ts
index c21e979ef..3d748deaf 100644
--- a/knox-token-management-ui/token-management/app/knox.token.ts
+++ b/knox-token-management-ui/token-management/app/session.information.ts
@@ -15,14 +15,10 @@
  * limitations under the License.
  */
 
-import {Metadata} from './metadata';
-
-export class KnoxToken {
-    tokenId: string;
-    issueTime: string;
-    issueTimeLong: number;
-    expiration: string;
-    expirationLong: number;
-    maxLifetime: string;
-    metadata: Metadata;
+export class SessionInformation {
+    user: string;
+    logoutUrl: string;
+    logoutPageUrl: string;
+    globalLgoutPageUrl: string;
+    canSeeAllTokens: boolean;
 }
diff --git 
a/knox-token-management-ui/token-management/app/token.management.component.html 
b/knox-token-management-ui/token-management/app/token.management.component.html
index 9275df06e..cda4859e8 100644
--- 
a/knox-token-management-ui/token-management/app/token.management.component.html
+++ 
b/knox-token-management-ui/token-management/app/token.management.component.html
@@ -19,8 +19,20 @@
         <button type="button" title="Refresh Knox Tokens" 
(click)="fetchKnoxTokens();">
             <span class="glyphicon glyphicon-refresh"></span>
         </button>
+        <span style="float: right;">
+            <mat-slide-toggle (change)="onChangeShowDisabledCookies($event)" 
[(ngModel)]="showDisabledKnoxSsoCookies">
+              Show Disabled KnoxSSO Cookies
+            </mat-slide-toggle>
+        </span>
     </div>
 
+    <div *ngIf="userCanSeeAllTokens()">
+        <span style="float: right;">
+            <mat-slide-toggle (change)="onChangeShowMyTokensOnly($event)" 
[(ngModel)]="showMyTokensOnly">
+              Show My Tokens Only
+            </mat-slide-toggle>
+        </span>
+    </div>
 
     <div class="table-responsive" style="width:100%; overflow: auto; 
overflow-y: auto; padding: 10px 0px 0px 0px;">
         <mat-form-field [floatLabel]="always" appearance="fill" #search>
@@ -29,9 +41,28 @@
         </mat-form-field>
 
         <mat-table [dataSource]="knoxTokens" matSort #knoxTokensSort="matSort">
+            <ng-container matColumnDef="select">
+                <mat-header-cell *matHeaderCellDef>
+                    <mat-checkbox (change)="$event ? masterToggle() : null"
+                        [checked]="selection.hasValue() && isAllSelected()"
+                        [indeterminate]="selection.hasValue() && 
!isAllSelected()">
+                    </mat-checkbox>
+                </mat-header-cell>
+                <mat-cell *matCellDef="let knoxToken">
+                    <mat-checkbox *ngIf="!isDisabledKnoxSSoCookie(knoxToken)"
+                        (click)="$event.stopPropagation()"
+                        (change)="$event ? onRowSelectionChange(knoxToken) : 
null"
+                        [checked]="selection.isSelected(knoxToken)">
+                    </mat-checkbox>
+                </mat-cell>
+            </ng-container>
+
             <ng-container matColumnDef="tokenId">
                 <mat-header-cell *matHeaderCellDef mat-sort-header="tokenId" 
style="text-align: center; justify-content: center;">Token ID</mat-header-cell>
-                <mat-cell *matCellDef="let knoxToken" style="text-align: 
center; justify-content: center;">{{knoxToken.tokenId}}</mat-cell>
+                <mat-cell *matCellDef="let knoxToken" style="text-align: 
center; justify-content: center;">
+                  <div 
*ngIf="knoxToken.metadata.enabled">{{knoxToken.tokenId}}</div>
+                  <div *ngIf="!knoxToken.metadata.enabled" 
style="color:orange">{{knoxToken.tokenId}}</div>
+                </mat-cell>
             </ng-container>
 
             <ng-container matColumnDef="issued">
@@ -63,8 +94,8 @@
             <ng-container matColumnDef="knoxSso">
                 <mat-header-cell *matHeaderCellDef mat-sort-header="knoxSso" 
style="text-align: center; justify-content: center;">KnoxSSO</mat-header-cell>
                 <mat-cell *matCellDef="let knoxToken" style="text-align: 
center; justify-content: center;">
-                  <img *ngIf="isKnoxSSoCookie(knoxToken)" 
src="assets/green_checkmark.svg" style="height:20px; width:auto" />
-                  <img *ngIf="!isKnoxSSoCookie(knoxToken)" 
src="assets/red_cross_circle.svg" style="height:20px; width:auto" />
+                  <img *ngIf="isKnoxSsoCookie(knoxToken)" 
src="assets/green_checkmark.svg" style="height:20px; width:auto" />
+                  <img *ngIf="!isKnoxSsoCookie(knoxToken)" 
src="assets/red_cross_circle.svg" style="height:20px; width:auto" />
                 </mat-cell>
             </ng-container>
 
@@ -88,9 +119,9 @@
                 <mat-header-cell *matHeaderCellDef style="text-align: center; 
justify-content: center;">Actions</mat-header-cell>
                 <mat-cell *matCellDef="let knoxToken">
                     <button *ngIf="knoxToken.metadata.enabled && 
!isTokenExpired(knoxToken.expirationLong)" 
(click)="disableToken(knoxToken.tokenId);">Disable</button>
-                    <button *ngIf="!isKnoxSSoCookie(knoxToken) && 
!knoxToken.metadata.enabled && !isTokenExpired(knoxToken.expirationLong)" 
(click)="enableToken(knoxToken.tokenId);">Enable</button>
-                    <button *ngIf="!isKnoxSSoCookie(knoxToken)" 
(click)="revokeToken(knoxToken.tokenId);">Revoke</button>
-                    <p *ngIf="isKnoxSSoCookie(knoxToken) && 
!knoxToken.metadata.enabled" style="color:orange">Previously Disabled SSO 
Cookie!</p>
+                    <button *ngIf="!isKnoxSsoCookie(knoxToken) && 
!knoxToken.metadata.enabled && !isTokenExpired(knoxToken.expirationLong)" 
(click)="enableToken(knoxToken.tokenId);">Enable</button>
+                    <button *ngIf="!isKnoxSsoCookie(knoxToken)" 
(click)="revokeToken(knoxToken.tokenId);">Revoke</button>
+                    <p *ngIf="isDisabledKnoxSsoCookie(knoxToken)" 
style="color:orange">Previously Disabled SSO Cookie!</p>
                 </mat-cell>
             </ng-container>
 
@@ -100,5 +131,11 @@
         </mat-table>
         <mat-paginator #knoxTokensPaginator [pageSizeOptions]="[5, 10, 25, 
100]" [pageSize]="25" [showFirstLastButtons]="true"></mat-paginator>
     </div>
+
+    <div>
+        <button *ngIf="showDisableSelectedTokensButton" type="button" 
(click)="disableSelectedTokens();">&nbsp;Disable Selected 
Tokens&nbsp;&nbsp;</button>
+        <button *ngIf="showEnableSelectedTokensButton" type="button" 
(click)="enableSelectedTokens();">&nbsp;Enable Selected 
Tokens&nbsp;&nbsp;</button>
+        <button *ngIf="showRevokeSelectedTokensButton" type="button" 
(click)="revokeSelectedTokens();">&nbsp;Revoke Selected Tokens&nbsp;</button>
+    </div>
 </div>
 
diff --git 
a/knox-token-management-ui/token-management/app/token.management.component.ts 
b/knox-token-management-ui/token-management/app/token.management.component.ts
index 1f26cd139..6c73db0bc 100644
--- 
a/knox-token-management-ui/token-management/app/token.management.component.ts
+++ 
b/knox-token-management-ui/token-management/app/token.management.component.ts
@@ -20,6 +20,8 @@ import {KnoxToken} from './knox.token';
 import {MatTableDataSource} from '@angular/material/table';
 import {MatPaginator} from '@angular/material/paginator';
 import {MatSort} from '@angular/material/sort';
+import {MatSlideToggleChange} from '@angular/material/slide-toggle';
+import {SelectionModel} from '@angular/cdk/collections';
 
 @Component({
     selector: 'app-token-management',
@@ -33,21 +35,24 @@ export class TokenManagementComponent implements OnInit {
     tokenGenerationPageURL = window.location.pathname.replace(new 
RegExp('token-management/.*'), 'token-generation/index.html');
 
     userName: string;
+    canSeeAllTokens: boolean;
     knoxTokens: MatTableDataSource<KnoxToken> = new MatTableDataSource();
+    selection = new SelectionModel<KnoxToken>(true, []);
+    allKnoxTokens: KnoxToken[];
 
-    displayedColumns = ['tokenId', 'issued', 'expires', 'userName', 
'impersonated', 'knoxSso', 'comment', 'metadata', 'actions'];
+    displayedColumns = ['select', 'tokenId', 'issued', 'expires', 'userName', 
'impersonated', 'knoxSso', 'comment', 'metadata', 'actions'];
     @ViewChild('knoxTokensPaginator') paginator: MatPaginator;
     @ViewChild('knoxTokensSort') sort: MatSort = new MatSort();
 
-    toggleBoolean(propertyName: string) {
-        this[propertyName] = !this[propertyName];
-    }
+    showDisabledKnoxSsoCookies: boolean;
+    showMyTokensOnly: boolean;
 
-    enableServiceText(enableServiceText: string) {
-        this[enableServiceText] = true;
-    }
+    showDisableSelectedTokensButton: boolean;
+    showEnableSelectedTokensButton: boolean;
+    showRevokeSelectedTokensButton: boolean;
 
     constructor(private tokenManagementService: TokenManagementService) {
+        this.showDisabledKnoxSsoCookies = true;
         let isMatch: (record: KnoxToken, filter: String) => boolean = (record, 
filter) => {
           let normalizedFilter = filter.trim().toLocaleLowerCase();
           let matchesTokenId = 
record.tokenId.toLocaleLowerCase().includes(normalizedFilter);
@@ -83,9 +88,23 @@ export class TokenManagementComponent implements OnInit {
         };
     }
 
+    onChangeShowDisabledCookies(value: MatSlideToggleChange) {
+        this.showDisabledKnoxSsoCookies = value.checked;
+        this.actualizeTokensToDisplay();
+    }
+
+    onChangeShowMyTokensOnly(value: MatSlideToggleChange) {
+        this.showMyTokensOnly = value.checked;
+        this.actualizeTokensToDisplay();
+    }
+
     ngOnInit(): void {
         console.debug('TokenManagementComponent --> ngOnInit()');
-        this.tokenManagementService.getUserName().then(userName => 
this.setUserName(userName));
+        this.tokenManagementService.getSessionInformation()
+            .then(sessionInformation => {
+                 this.canSeeAllTokens = sessionInformation.canSeeAllTokens;
+                 this.setUserName(sessionInformation.user);
+            });
     }
 
     setUserName(userName: string) {
@@ -93,26 +112,81 @@ export class TokenManagementComponent implements OnInit {
         this.fetchKnoxTokens();
     }
 
+    userCanSeeAllTokens(): boolean {
+        return this.canSeeAllTokens;
+    }
+
     fetchKnoxTokens(): void {
-        this.tokenManagementService.getKnoxTokens(this.userName).then(tokens 
=> this.knoxTokens.data = tokens);
-        setTimeout(() => {
+        this.tokenManagementService.getKnoxTokens(this.userName, 
this.canSeeAllTokens)
+            .then(tokens => this.updateTokens(tokens));
+    }
+
+    private isMyToken(token: KnoxToken): boolean {
+           return token.metadata.userName === this.userName || 
(token.metadata.createdBy && token.metadata.createdBy === this.userName);
+    }
+
+    private isDisabledKnoxSsoCookie(token: KnoxToken): boolean {
+        return token.metadata.knoxSsoCookie && !token.metadata.enabled;
+    }
+
+    private updateTokens(tokens: KnoxToken[]): void {
+        this.allKnoxTokens = tokens;
+        this.selection.clear();
+        this.showHideBatchOperations();
+        this.actualizeTokensToDisplay();
+    }
+
+    private actualizeTokensToDisplay(): void {
+           let tokensToDisplay = this.allKnoxTokens;
+
+        if (!this.showDisabledKnoxSsoCookies) {
+            tokensToDisplay = tokensToDisplay.filter(token => 
!this.isDisabledKnoxSsoCookie(token));
+        }
+
+        if (this.showMyTokensOnly) {
+             tokensToDisplay = tokensToDisplay.filter(token => 
this.isMyToken(token));
+         }
+
+         this.knoxTokens.data = tokensToDisplay;
+
+         setTimeout(() => {
             this.knoxTokens.paginator = this.paginator;
             this.knoxTokens.sort = this.sort;
-        });
+         });
     }
 
     disableToken(tokenId: string) {
         this.tokenManagementService.setEnabledDisabledFlag(false, 
tokenId).then((response: string) => this.fetchKnoxTokens());
     }
 
+    disableSelectedTokens(): void {
+        this.tokenManagementService.setEnabledDisabledFlagsInBatch(false, 
this.getSelectedTokenIds())
+            .then((response: string) => this.fetchKnoxTokens());
+    }
+
+    private getSelectedTokenIds(): string[] {
+        let selectedTokenIds = [] as string[];
+        this.selection.selected.forEach(token => 
selectedTokenIds.push(token.tokenId));
+        return selectedTokenIds;
+    }
+
     enableToken(tokenId: string) {
         this.tokenManagementService.setEnabledDisabledFlag(true, 
tokenId).then((response: string) => this.fetchKnoxTokens());
     }
 
+    enableSelectedTokens(): void {
+        this.tokenManagementService.setEnabledDisabledFlagsInBatch(true, 
this.getSelectedTokenIds())
+            .then((response: string) => this.fetchKnoxTokens());
+    }
+
     revokeToken(tokenId: string) {
         this.tokenManagementService.revokeToken(tokenId).then((response: 
string) => this.fetchKnoxTokens());
     }
 
+    revokeSelectedTokens() {
+        
this.tokenManagementService.revokeTokensInBatch(this.getSelectedTokenIds()).then((response:
 string) => this.fetchKnoxTokens());
+    }
+
     gotoTokenGenerationPage() {
         window.open(this.tokenGenerationPageURL, '_blank');
     }
@@ -138,14 +212,60 @@ export class TokenManagementComponent implements OnInit {
       return Array.from(mdMap);
     }
 
-    isKnoxSSoCookie(knoxToken: KnoxToken): boolean {
+    isKnoxSsoCookie(knoxToken: KnoxToken): boolean {
       return knoxToken.metadata.knoxSsoCookie;
     }
 
+    isDisabledKnoxSSoCookie(knoxToken: KnoxToken): boolean {
+      return this.isKnoxSsoCookie(knoxToken) && !knoxToken.metadata.enabled;
+    }
+
     applyFilter(filterValue: string) {
         filterValue = filterValue.trim(); // Remove whitespace
         filterValue = filterValue.toLowerCase(); // Datasource defaults to 
lowercase matches
         this.knoxTokens.filter = filterValue;
     }
 
+    /** Whether the number of selected elements matches the total number of 
rows. */
+    isAllSelected(): boolean {
+      const numSelected = this.selection.selected.length;
+      const numRows = this.knoxTokens.filteredData.length;
+      return numSelected === numRows;
+    }
+
+    /** Selects all rows if they are not all selected; otherwise clear 
selection. */
+    masterToggle(): void {
+        if (this.isAllSelected()) {
+            this.selection.clear();
+        } else {
+            this.knoxTokens.filteredData.forEach(row => {
+                   if (!this.isDisabledKnoxSsoCookie(row)) {
+                    this.selection.select(row);
+                }
+            });
+        }
+        this.showHideBatchOperations();
+    }
+
+    onRowSelectionChange(knoxToken: KnoxToken): void {
+        this.selection.toggle(knoxToken);
+        this.showHideBatchOperations();
+    }
+
+    showHideBatchOperations() {
+           if (this.selection.isEmpty()) {
+                   this.showDisableSelectedTokensButton = false;
+            this.showEnableSelectedTokensButton = false;
+            this.showRevokeSelectedTokensButton = false;
+           } else {
+            this.showDisableSelectedTokensButton = true;
+            this.showEnableSelectedTokensButton = true;
+            this.showRevokeSelectedTokensButton = 
this.selectionHasZeroKnoxSsoCookie(); // KnoxSSO cookies must not be revoked
+           }
+    }
+
+    private selectionHasZeroKnoxSsoCookie(): boolean {
+        return this.selection.selected.every(token => 
!token.metadata.knoxSsoCookie);
+    }
+
 }
diff --git 
a/knox-token-management-ui/token-management/app/token.management.service.ts 
b/knox-token-management-ui/token-management/app/token.management.service.ts
index f38b25472..3abef06a3 100644
--- a/knox-token-management-ui/token-management/app/token.management.service.ts
+++ b/knox-token-management-ui/token-management/app/token.management.service.ts
@@ -21,23 +21,29 @@ import Swal from 'sweetalert2';
 import 'rxjs/add/operator/toPromise';
 
 import {KnoxToken} from './knox.token';
+import {SessionInformation} from './session.information';
 
 @Injectable()
 export class TokenManagementService {
     sessionUrl = window.location.pathname.replace(new 
RegExp('token-management/.*'), 'session/api/v1/sessioninfo');
     apiUrl = window.location.pathname.replace(new 
RegExp('token-management/.*'), 'knoxtoken/api/v1/token/');
+    getAllKnoxTokensUrl = this.apiUrl + 'getUserTokens?allTokens=true';
     getKnoxTokensUrl = this.apiUrl + 'getUserTokens?userNameOrCreatedBy=';
     enableKnoxTokenUrl = this.apiUrl + 'enable';
+    enableKnoxTokensBatchUrl = this.apiUrl + 'enableTokens';
     disableKnoxTokenUrl = this.apiUrl + 'disable';
+    disableKnoxTokensBatchUrl = this.apiUrl + 'disableTokens';
     revokeKnoxTokenUrl = this.apiUrl + 'revoke';
+    revokeKnoxTokensBatchUrl = this.apiUrl + 'revokeTokens';
     getTssStatusUrl = this.apiUrl + 'getTssStatus';
 
     constructor(private http: HttpClient) {}
 
-    getKnoxTokens(userName: string): Promise<KnoxToken[]> {
+    getKnoxTokens(userName: string, canSeeAllTokens: boolean): 
Promise<KnoxToken[]> {
         let headers = new HttpHeaders();
         headers = this.addJsonHeaders(headers);
-        return this.http.get(this.getKnoxTokensUrl + userName, { headers: 
headers})
+        let url = canSeeAllTokens ? this.getAllKnoxTokensUrl : 
(this.getKnoxTokensUrl + userName);
+        return this.http.get(url, { headers: headers})
             .toPromise()
             .then(response => response['tokens'] as KnoxToken[])
             .catch((err: HttpErrorResponse) => {
@@ -68,6 +74,24 @@ export class TokenManagementService {
             });
     }
 
+    setEnabledDisabledFlagsInBatch(enable: boolean, tokenIds: string[]): 
Promise<string> {
+        let xheaders = new HttpHeaders();
+        xheaders = this.addJsonHeaders(xheaders);
+        let urlToUse = enable ? this.enableKnoxTokensBatchUrl : 
this.disableKnoxTokensBatchUrl;
+        return this.http.put(urlToUse, JSON.stringify(tokenIds), {headers: 
xheaders, responseType: 'text'})
+            .toPromise()
+            .then(response => response)
+            .catch((err: HttpErrorResponse) => {
+                console.debug('TokenManagementService --> 
setEnabledDisabledFlagsInBatch() --> ' + urlToUse
+                              + '\n  error: ' + err.status + ' ' + 
err.message);
+                if (err.status === 401) {
+                    window.location.assign(document.location.pathname);
+                } else {
+                    return this.handleError(err);
+                }
+            });
+    }
+
     revokeToken(tokenId: string) {
         let xheaders = new HttpHeaders();
         xheaders = this.addJsonHeaders(xheaders);
@@ -85,14 +109,32 @@ export class TokenManagementService {
             });
     }
 
-    getUserName(): Promise<string> {
+    revokeTokensInBatch(tokenIds: string[]) {
+        let xheaders = new HttpHeaders();
+        xheaders = this.addJsonHeaders(xheaders);
+        return this.http.request('DELETE', this.revokeKnoxTokensBatchUrl,
+                                 {headers: xheaders, body: 
JSON.stringify(tokenIds), responseType: 'text'})
+            .toPromise()
+            .then(response => response)
+            .catch((err: HttpErrorResponse) => {
+                console.debug('TokenManagementService --> 
revokeTokensInBatch() --> ' + this.revokeKnoxTokensBatchUrl
+                              + '\n  error: ' + err.status + ' ' + 
err.message);
+                if (err.status === 401) {
+                    window.location.assign(document.location.pathname);
+                } else {
+                    return this.handleError(err);
+                }
+            });
+    }
+
+    getSessionInformation(): Promise<SessionInformation> {
         let headers = new HttpHeaders();
         headers = this.addJsonHeaders(headers);
         return this.http.get(this.sessionUrl, { headers: headers})
             .toPromise()
-            .then(response => response['sessioninfo'].user as string)
+            .then(response => response['sessioninfo'] as SessionInformation)
             .catch((err: HttpErrorResponse) => {
-                console.debug('TokenManagementService --> getUserName() --> ' 
+ this.sessionUrl + '\n  error: ' + err.message);
+                console.debug('TokenManagementService --> 
getSessionInformation() --> ' + this.sessionUrl + '\n  error: ' + err.message);
                 if (err.status === 401) {
                     window.location.assign(document.location.pathname);
                 } else {

Reply via email to