You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by sm...@apache.org on 2022/03/07 10:10:11 UTC
[knox] branch master updated: KNOX-2713 - Allowing end-users to customize 'user limit exceeded' action when creating Knox tokens (#543)
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 848689b KNOX-2713 - Allowing end-users to customize 'user limit exceeded' action when creating Knox tokens (#543)
848689b is described below
commit 848689b0b0e8c6a969dd5bdd2f85d40cf4047a73
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Mon Mar 7 11:10:05 2022 +0100
KNOX-2713 - Allowing end-users to customize 'user limit exceeded' action when creating Knox tokens (#543)
---
.../gateway/service/knoxtoken/TokenResource.java | 22 ++++++++++++--
.../service/knoxtoken/TokenServiceMessages.java | 3 ++
.../knoxtoken/TokenServiceResourceTest.java | 35 +++++++++++++++++-----
3 files changed, 51 insertions(+), 9 deletions(-)
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 d083c8e..96c189e 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
@@ -32,6 +32,7 @@ import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
@@ -117,6 +118,7 @@ public class TokenResource {
private static final String TSS_MAXIMUM_LIFETIME_TEXT = "maximumLifetimeText";
private static final String LIFESPAN_INPUT_ENABLED_PARAM = "knox.token.lifespan.input.enabled";
private static final String LIFESPAN_INPUT_ENABLED_TEXT = "lifespanInputEnabled";
+ static final String KNOX_TOKEN_USER_LIMIT_EXCEEDED_ACTION = "knox.token.user.limit.exceeded.action";
private static final long TOKEN_TTL_DEFAULT = 30000L;
static final String TOKEN_API_PATH = "knoxtoken/api/v1";
static final String RESOURCE_PATH = TOKEN_API_PATH + "/token";
@@ -150,6 +152,9 @@ public class TokenResource {
private int tokenLimitPerUser;
+ enum UserLimitExceededAction {REMOVE_OLDEST, RETURN_ERROR};
+ private UserLimitExceededAction userLimitExceededAction = UserLimitExceededAction.RETURN_ERROR;
+
private List<String> allowedRenewers;
@Context
@@ -246,6 +251,11 @@ public class TokenResource {
tokenMAC = new TokenMAC(gatewayConfig.getKnoxTokenHashAlgorithm(), aliasService.getPasswordFromAliasForGateway(TokenMAC.KNOX_TOKEN_HASH_KEY_ALIAS_NAME));
tokenLimitPerUser = gatewayConfig.getMaximumNumberOfTokensPerUser();
+ final String userLimitExceededActionParam = context.getInitParameter(KNOX_TOKEN_USER_LIMIT_EXCEEDED_ACTION);
+ if (userLimitExceededActionParam != null) {
+ userLimitExceededAction = UserLimitExceededAction.valueOf(userLimitExceededActionParam);
+ log.generalInfoMessage("Configured Knox Token user limit exceeded action = " + userLimitExceededAction.name());
+ }
String renewIntervalValue = context.getInitParameter(TOKEN_EXP_RENEWAL_INTERVAL);
if (renewIntervalValue != null && !renewIntervalValue.isEmpty()) {
@@ -654,9 +664,17 @@ public class TokenResource {
if (tokenStateService != null) {
if (tokenLimitPerUser != -1) { // if -1 => unlimited tokens for all users
- if (tokenStateService.getTokens(p.getName()).size() >= tokenLimitPerUser) {
+ final Collection<KnoxToken> userTokens = tokenStateService.getTokens(p.getName());
+ if (userTokens.size() >= tokenLimitPerUser) {
log.tokenLimitExceeded(p.getName());
- return Response.status(Response.Status.FORBIDDEN).entity("{ \"Unable to get token - token limit exceeded.\" }").build();
+ if (UserLimitExceededAction.RETURN_ERROR == userLimitExceededAction) {
+ return Response.status(Response.Status.FORBIDDEN).entity("{ \"Unable to get token - token limit exceeded.\" }").build();
+ } else {
+ // userTokens is an ordered collection (by issue time) -> the first element is the oldest one
+ final String oldestTokenId = userTokens.iterator().next().getTokenId();
+ log.generalInfoMessage(String.format(Locale.getDefault(), "Revoking %s's oldest token %s ...", p.getName(), Tokens.getTokenIDDisplayText(oldestTokenId)));
+ revoke(oldestTokenId);
+ }
}
}
}
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 8cae4b6..6208cbc 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
@@ -86,4 +86,7 @@ public interface TokenServiceMessages {
@Message( level = MessageLevel.ERROR, text = "Unable to get token for user {0}: token limit exceeded")
void tokenLimitExceeded(String userName);
+
+ @Message( level = MessageLevel.INFO, text = "{0}")
+ void generalInfoMessage(String message);
}
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 d895a53..e74f2e2 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
@@ -19,6 +19,7 @@ package org.apache.knox.gateway.service.knoxtoken;
import static org.apache.knox.gateway.config.impl.GatewayConfigImpl.KNOX_TOKEN_USER_LIMIT;
import static org.apache.knox.gateway.config.impl.GatewayConfigImpl.KNOX_TOKEN_USER_LIMIT_DEFAULT;
+import static org.apache.knox.gateway.service.knoxtoken.TokenResource.KNOX_TOKEN_USER_LIMIT_EXCEEDED_ACTION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -984,12 +985,12 @@ public class TokenServiceResourceTest {
@Test
public void testConfiguredTokenLimitPerUser() throws Exception {
- testLimitingTokensPerUser(String.valueOf(KNOX_TOKEN_USER_LIMIT_DEFAULT), KNOX_TOKEN_USER_LIMIT_DEFAULT);
+ testLimitingTokensPerUser(KNOX_TOKEN_USER_LIMIT_DEFAULT, KNOX_TOKEN_USER_LIMIT_DEFAULT);
}
@Test
public void testUnlimitedTokensPerUser() throws Exception {
- testLimitingTokensPerUser(String.valueOf("-1"), 100);
+ testLimitingTokensPerUser(-1, 100);
}
@Test
@@ -1023,16 +1024,32 @@ public class TokenServiceResourceTest {
@Test
public void testTokenLimitPerUserExceeded() throws Exception {
try {
- testLimitingTokensPerUser(String.valueOf("10"), 11);
+ testLimitingTokensPerUser(10, 11);
fail("Exception should have been thrown");
} catch (Exception e) {
assertTrue(e.getMessage().contains("Unable to get token - token limit exceeded."));
}
}
- private void testLimitingTokensPerUser(String configuredLimit, int numberOfTokens) throws Exception {
+ @Test
+ public void testTokenLimitPerUserExceededShouldRevokeOldestToken() throws Exception {
+ try {
+ testLimitingTokensPerUser(10, 11, true);
+ } catch (Exception e) {
+ fail("Exception should NOT have been thrown");
+ }
+ }
+
+ private void testLimitingTokensPerUser(int configuredLimit, int numberOfTokens) throws Exception {
+ testLimitingTokensPerUser(configuredLimit, numberOfTokens, false);
+ }
+
+ private void testLimitingTokensPerUser(int configuredLimit, int numberOfTokens, boolean revokeOldestToken) throws Exception {
final Map<String, String> contextExpectations = new HashMap<>();
- contextExpectations.put(KNOX_TOKEN_USER_LIMIT, configuredLimit);
+ contextExpectations.put(KNOX_TOKEN_USER_LIMIT, String.valueOf(configuredLimit));
+ if (revokeOldestToken) {
+ contextExpectations.put(KNOX_TOKEN_USER_LIMIT_EXCEEDED_ACTION, TokenResource.UserLimitExceededAction.REMOVE_OLDEST.name());
+ }
configureCommonExpectations(contextExpectations, Boolean.TRUE);
final TokenResource tr = new TokenResource();
@@ -1041,7 +1058,7 @@ public class TokenServiceResourceTest {
tr.init();
for (int i = 0; i < numberOfTokens; i++) {
- final Response getTokenResponse = tr.doGet();
+ final Response getTokenResponse = Subject.doAs(createTestSubject(USER_NAME), (PrivilegedAction<Response>) () -> tr.doGet());
if (getTokenResponse.getStatus() != Response.Status.OK.getStatusCode()) {
throw new Exception(getTokenResponse.getEntity().toString());
}
@@ -1049,7 +1066,7 @@ public class TokenServiceResourceTest {
final Response getKnoxTokensResponse = tr.getUserTokens(USER_NAME);
final Collection<String> tokens = ((Map<String, Collection<String>>) JsonUtils.getObjectFromJsonString(getKnoxTokensResponse.getEntity().toString()))
.get("tokens");
- assertEquals(tokens.size(), numberOfTokens);
+ assertEquals(tokens.size(), revokeOldestToken ? configuredLimit : numberOfTokens);
}
/**
@@ -1395,6 +1412,10 @@ public class TokenServiceResourceTest {
@Override
public void revokeToken(String tokenId) {
+ issueTimes.remove(tokenId);
+ expirationData.remove(tokenId);
+ maxLifetimes.remove(tokenId);
+ tokenMetadata.remove(tokenId);
}
@Override