KNOX-1314 - SSOCookieProvider should be able to derive a default provider URL
authorLarry McCay <lmccay@HW14155.home>
Thu, 17 May 2018 16:52:20 +0000 (12:52 -0400)
committerLarry McCay <lmccay@HW14155.home>
Thu, 17 May 2018 16:52:58 +0000 (12:52 -0400)
gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java

index 70efa8c..5f43395 100644 (file)
@@ -49,7 +49,7 @@ public interface JWTMessages {
   @Message( level = MessageLevel.DEBUG, text = "Sending redirect to: {0}" )
   void sendRedirectToLoginURL(String loginURL);
 
-  @Message( level = MessageLevel.ERROR, text = "Required configuration element for authentication provider is missing." )
+  @Message( level = MessageLevel.WARN, text = "Configuration for authentication provider URL is missing - will derive default URL." )
   void missingAuthenticationProviderUrlConfiguration();
 
   @Message( level = MessageLevel.DEBUG, text = "{0} Cookie has been found and is being processed." )
index 1a43e3a..8537ee0 100644 (file)
@@ -42,6 +42,9 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter {
   public static final String SSO_EXPECTED_AUDIENCES = "sso.expected.audiences";
   public static final String SSO_AUTHENTICATION_PROVIDER_URL = "sso.authentication.provider.url";
   public static final String SSO_VERIFICATION_PEM = "sso.token.verification.pem";
+  public static final String X_FORWARDED_HOST = "X-Forwarded-Host";
+  public static final String X_FORWARDED_PORT = "X-Forwarded-Port";
+  public static final String X_FORWARDED_PROTO = "X-Forwarded-Proto";
 
   private static final String ORIGINAL_URL_QUERY_PARAM = "originalUrl=";
   private static final String DEFAULT_SSO_COOKIE_NAME = "hadoop-jwt";
@@ -72,7 +75,6 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter {
     authenticationProviderUrl = filterConfig.getInitParameter(SSO_AUTHENTICATION_PROVIDER_URL);
     if (authenticationProviderUrl == null) {
       log.missingAuthenticationProviderUrlConfiguration();
-      throw new ServletException("Required authentication provider URL is missing.");
     }
 
     // token verification pem
@@ -169,6 +171,9 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter {
    */
   protected String constructLoginURL(HttpServletRequest request) {
     String delimiter = "?";
+    if (authenticationProviderUrl == null) {
+       authenticationProviderUrl = deriveDefaultAuthenticationProviderUrl(request);
+    }
     if (authenticationProviderUrl.contains("?")) {
       delimiter = "&";
     }
@@ -178,9 +183,35 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter {
     return loginURL;
   }
 
+  /**
+   * Derive a provider URL from the request assuming that the
+   * KnoxSSO endpoint is local to the endpoint serving this request.
+   * @param request
+   * @return
+   */
+  public String deriveDefaultAuthenticationProviderUrl(HttpServletRequest request) {
+    String scheme = null;
+    String host = null;
+    int port = 0;
+    if (!beingProxied(request)) {
+      scheme = request.getScheme();
+      host = request.getServerName();
+      port = request.getServerPort();
+    }
+    else {
+      scheme = request.getHeader(X_FORWARDED_PROTO);
+      host = request.getHeader(X_FORWARDED_HOST);
+      port = Integer.parseInt(request.getHeader(X_FORWARDED_PORT));
+    }
+    return scheme + "://" + host + ":" + port + "/" + "gateway/knoxsso/api/v1/websso";
+  }
+
+  private boolean beingProxied(HttpServletRequest request) {
+    return (request.getHeader(X_FORWARDED_HOST) != null);
+  }
+
   private String getOriginalQueryString(HttpServletRequest request) {
     String originalQueryString = request.getQueryString();
     return (originalQueryString == null) ? "" : "?" + originalQueryString;
   }
-
 }
index 50a44ce..3aa4cb4 100644 (file)
@@ -102,12 +102,10 @@ public class SSOCookieProviderTest extends AbstractJWTFilterTest {
       Properties props = getProperties();
       props.remove("sso.authentication.provider.url");
       handler.init(new TestFilterConfig(props));
-
-      fail("Servlet exception should have been thrown.");
-
     } catch (ServletException se) {
-      // expected - let's ensure it mentions the missing authentication provider URL
-      se.getMessage().contains("authentication provider URL is missing");
+      // no longer expected - let's ensure it mentions the missing authentication provider URL
+      fail("Servlet exception should have been thrown.");
+      se.printStackTrace();
     }
   }
 
@@ -143,6 +141,48 @@ public class SSOCookieProviderTest extends AbstractJWTFilterTest {
     Assert.assertEquals("https://localhost:8443/authserver?originalUrl=" + SERVICE_URL, loginURL);
   }
 
+  @Test
+  public void testDefaultAuthenticationProviderURL() throws Exception {
+    Properties props = new Properties();
+    handler.init(new TestFilterConfig(props));
+
+    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+    EasyMock.expect(request.getRequestURL()).andReturn(new StringBuffer(SERVICE_URL)).anyTimes();
+    EasyMock.expect(request.getQueryString()).andReturn(null);
+    EasyMock.expect(request.getScheme()).andReturn("https").anyTimes();
+    EasyMock.expect(request.getServerName()).andReturn("localhost").anyTimes();
+    EasyMock.expect(request.getServerPort()).andReturn(8443).anyTimes();
+    EasyMock.replay(request);
+
+    String providerURL = ((TestSSOCookieFederationProvider) handler).deriveDefaultAuthenticationProviderUrl(request);
+    Assert.assertNotNull("LoginURL should not be null.", providerURL);
+    Assert.assertEquals(providerURL, "https://localhost:8443/gateway/knoxsso/api/v1/websso");
+
+    String loginURL = ((TestSSOCookieFederationProvider) handler).testConstructLoginURL(request);
+    Assert.assertNotNull("LoginURL should not be null.", loginURL);
+    Assert.assertEquals(loginURL, "https://localhost:8443/gateway/knoxsso/api/v1/websso?originalUrl=" + SERVICE_URL);
+  }
+
+  @Test
+  public void testProxiedDefaultAuthenticationProviderURL() throws Exception {
+    Properties props = new Properties();
+    handler.init(new TestFilterConfig(props));
+
+    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+    EasyMock.expect(request.getRequestURL()).andReturn(new StringBuffer(SERVICE_URL)).anyTimes();
+    EasyMock.expect(request.getHeader(SSOCookieFederationFilter.X_FORWARDED_PROTO)).andReturn("https").anyTimes();
+    EasyMock.expect(request.getHeader(SSOCookieFederationFilter.X_FORWARDED_HOST)).andReturn("remotehost").anyTimes();
+    EasyMock.expect(request.getHeader(SSOCookieFederationFilter.X_FORWARDED_PORT)).andReturn("8443").anyTimes();
+    EasyMock.replay(request);
+
+    String providerURL = ((TestSSOCookieFederationProvider) handler).deriveDefaultAuthenticationProviderUrl(request);
+    Assert.assertNotNull("LoginURL should not be null.", providerURL);
+    Assert.assertEquals(providerURL, "https://remotehost:8443/gateway/knoxsso/api/v1/websso");
+
+    String loginURL = ((TestSSOCookieFederationProvider) handler).testConstructLoginURL(request);
+    Assert.assertNotNull("LoginURL should not be null.", loginURL);
+    Assert.assertEquals(loginURL, "https://remotehost:8443/gateway/knoxsso/api/v1/websso?originalUrl=" + SERVICE_URL);
+  }
 
   @Override
   protected String getVerificationPemProperty() {