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);
   }

Reply via email to