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 e1a746879 KNOX-3032 - Passcode use without token state service returns
401 (#902)
e1a746879 is described below
commit e1a746879cedeaf4401a905328cd382bdbb4eb85
Author: Sandor Molnar <[email protected]>
AuthorDate: Tue Apr 30 17:05:31 2024 +0200
KNOX-3032 - Passcode use without token state service returns 401 (#902)
---
.../provider/federation/jwt/JWTMessages.java | 3 ++
.../federation/jwt/filter/AbstractJWTFilter.java | 8 ++-
.../federation/JWTFederationFilterTest.java | 61 ++++++++++++++++++++++
3 files changed, 71 insertions(+), 1 deletion(-)
diff --git
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
index 967e56466..e6979921c 100644
---
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
+++
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
@@ -55,6 +55,9 @@ public interface JWTMessages {
@Message( level = MessageLevel.WARN, text = "Unable to verify token
expiration: {0}" )
void unableToVerifyExpiration(@StackTrace( level = MessageLevel.DEBUG)
Exception e);
+ @Message( level = MessageLevel.WARN, text = "Unable to verify passcode token
({0}) due to missing or incorrect token state service configuration.")
+ void unableToVerifyPasscodeToken(String tokenId);
+
@Message( level = MessageLevel.ERROR, text = "Unable to issue token: {0}" )
void unableToIssueToken(@StackTrace( level = MessageLevel.DEBUG) Exception
e);
diff --git
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
index b58ad5e42..e372e0d42 100644
---
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
+++
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
@@ -79,6 +79,9 @@ import com.nimbusds.jose.JWSHeader;
import org.apache.knox.gateway.util.Tokens;
public abstract class AbstractJWTFilter implements Filter {
+
+ public static final String TOKEN_STATE_SERVICE_DISABLED_ERROR = "Error in
token provider config: passcode use with knox.token.exp.server-managed set to
false.";
+
/**
* If specified, this configuration property refers to a value which the
issuer of a received
* token must match. Otherwise, the default value "KNOXSSO" is used
@@ -433,10 +436,10 @@ public abstract class AbstractJWTFilter implements Filter
{
final String passcode)
throws IOException, ServletException {
+ final String displayableTokenId = tokenId == null ? "N/A" :
Tokens.getTokenIDDisplayText(tokenId);
if (tokenStateService != null) {
try {
if (tokenId != null) {
- final String displayableTokenId =
Tokens.getTokenIDDisplayText(tokenId);
if (tokenIsStillValid(tokenId)) {
final TokenMetadata tokenMetadata = tokenStateService == null ?
null : tokenStateService.getTokenMetadata(tokenId);
if (isTokenEnabled(tokenMetadata)) {
@@ -473,6 +476,9 @@ public abstract class AbstractJWTFilter implements Filter {
log.unableToVerifyExpiration(e);
handleValidationError(request, response,
HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
}
+ } else {
+ log.unableToVerifyPasscodeToken(displayableTokenId);
+ handleValidationError(request, response,
HttpServletResponse.SC_UNAUTHORIZED, TOKEN_STATE_SERVICE_DISABLED_ERROR);
}
return false;
diff --git
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
index f17897384..6849bcf70 100644
---
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
+++
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
@@ -24,12 +24,18 @@ import static org.junit.Assert.assertEquals;
import java.util.Date;
import java.util.Properties;
+import javax.servlet.FilterConfig;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import
org.apache.knox.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
import
org.apache.knox.gateway.provider.federation.jwt.filter.JWTFederationFilter;
+import
org.apache.knox.gateway.provider.federation.jwt.filter.SignatureVerificationCache;
+import org.apache.knox.gateway.services.security.token.TokenMetadata;
+import org.apache.knox.gateway.services.security.token.TokenStateService;
import org.easymock.EasyMock;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -98,6 +104,61 @@ public class JWTFederationFilterTest extends
AbstractJWTFilterTest {
testCookieAuthSupport(true, "customCookie");
}
+ @Test
+ public void testVerifyPasscodeTokens() throws Exception {
+ testVerifyPasscodeTokens(true);
+ }
+
+ @Test
+ public void testVerifyPasscodeTokensTssDisabled() throws Exception {
+ testVerifyPasscodeTokens(false);
+ }
+
+ private void testVerifyPasscodeTokens(boolean tssEnabled) throws Exception {
+ final String topologyName = "jwt-topology";
+ final String tokenId = "4e0c548b-6568-4061-a3dc-62908087650a";
+ final String passcode = "0138aaed-ca2a-47f1-8ed8-e0c397596f95";
+ final String passcodeToken =
"UGFzc2NvZGU6VGtkVmQxbDZWVEJQUjBsMFRtcFZNazlETURCTlJGbDRURmRGZWxwSFRYUk9ha2sxVFVSbmQwOUVZekpPVkVKb09qcE5SRVY2VDBkR2FGcFhVWFJaTWtWNVdWTXdNRTR5V1hoTVZHaHNXa1JuZEZwVVFtcE5lbXN6VGxSck1scHFhekU9";
+
+ final TokenStateService tokenStateService =
EasyMock.createNiceMock(TokenStateService.class);
+
EasyMock.expect(tokenStateService.getTokenExpiration(tokenId)).andReturn(Long.MAX_VALUE).anyTimes();
+
+ final TokenMetadata tokenMetadata =
EasyMock.createNiceMock(TokenMetadata.class);
+ EasyMock.expect(tokenMetadata.isEnabled()).andReturn(true).anyTimes();
+
EasyMock.expect(tokenMetadata.getPasscode()).andReturn(passcodeToken).anyTimes();
+
EasyMock.expect(tokenStateService.getTokenMetadata(EasyMock.anyString())).andReturn(tokenMetadata).anyTimes();
+
+ final Properties filterConfigProps = getProperties();
+ filterConfigProps.put(TokenStateService.CONFIG_SERVER_MANAGED,
Boolean.toString(tssEnabled));
+ filterConfigProps.put(TestFilterConfig.TOPOLOGY_NAME_PROP, topologyName);
+ final FilterConfig filterConfig = new TestFilterConfig(filterConfigProps,
tokenStateService);
+ handler.init(filterConfig);
+
+ final HttpServletRequest request =
EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getRequestURL()).andReturn(new
StringBuffer(SERVICE_URL)).anyTimes();
+ EasyMock.expect(request.getHeader("Authorization")).andReturn("Basic " +
passcodeToken);
+
+ final HttpServletResponse response =
EasyMock.createNiceMock(HttpServletResponse.class);
+ if (!tssEnabled) {
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
AbstractJWTFilter.TOKEN_STATE_SERVICE_DISABLED_ERROR);
+ EasyMock.expectLastCall().once();
+ }
+ EasyMock.replay(tokenStateService, tokenMetadata, request, response);
+
+ SignatureVerificationCache.getInstance(topologyName,
filterConfig).recordSignatureVerification(passcode);
+
+ final TestFilterChain chain = new TestFilterChain();
+ handler.doFilter(request, response, chain);
+
+ EasyMock.verify(response);
+ if (tssEnabled) {
+ Assert.assertTrue(chain.doFilterCalled);
+ Assert.assertNotNull(chain.subject);
+ } else {
+ Assert.assertFalse(chain.doFilterCalled);
+ }
+ }
+
private void testCookieAuthSupport(boolean validCookie) throws Exception {
testCookieAuthSupport(validCookie, null);
}