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();"> Disable Selected
Tokens </button>
+ <button *ngIf="showEnableSelectedTokensButton" type="button"
(click)="enableSelectedTokens();"> Enable Selected
Tokens </button>
+ <button *ngIf="showRevokeSelectedTokensButton" type="button"
(click)="revokeSelectedTokens();"> Revoke Selected Tokens </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 {