You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by co...@apache.org on 2017/09/20 10:10:08 UTC

knox git commit: KNOX-1052 - Add some tests for the Knox SSO Service

Repository: knox
Updated Branches:
  refs/heads/master a5a88258b -> 2666894bc


KNOX-1052 - Add some tests for the Knox SSO Service


Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/2666894b
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/2666894b
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/2666894b

Branch: refs/heads/master
Commit: 2666894bc84281ed78890110ab15b009fa5f2830
Parents: a5a8825
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Sep 20 11:09:54 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Sep 20 11:09:54 2017 +0100

----------------------------------------------------------------------
 gateway-service-knoxsso/pom.xml                 |  11 +-
 .../gateway/service/knoxsso/WebSSOResource.java |  20 +-
 .../service/knoxsso/WebSSOResourceTest.java     | 304 ++++++++++++++++++-
 3 files changed, 308 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/2666894b/gateway-service-knoxsso/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/pom.xml b/gateway-service-knoxsso/pom.xml
index f5018a2..e6b6ca5 100644
--- a/gateway-service-knoxsso/pom.xml
+++ b/gateway-service-knoxsso/pom.xml
@@ -59,9 +59,10 @@
       <artifactId>gateway-test-utils</artifactId>
       <scope>test</scope>
     </dependency>
-      <dependency>
-          <groupId>org.easymock</groupId>
-          <artifactId>easymock</artifactId>
-          <scope>test</scope>
-      </dependency>  </dependencies>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/knox/blob/2666894b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
index 7cc5378..0d9e6dd 100644
--- a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
+++ b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
@@ -23,6 +23,7 @@ import java.net.URISyntaxException;
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -74,14 +75,14 @@ public class WebSSOResource {
   private long tokenTTL = 30000l;
   private String whitelist = null;
   private String domainSuffix = null;
-  private String[] targetAudiences = null;
+  private List<String> targetAudiences = new ArrayList<>();
   private boolean enableSession = false;
 
   @Context
-  private HttpServletRequest request;
+  HttpServletRequest request;
 
   @Context
-  private HttpServletResponse response;
+  HttpServletResponse response;
 
   @Context
   ServletContext context;
@@ -124,7 +125,10 @@ public class WebSSOResource {
 
     String audiences = context.getInitParameter(SSO_COOKIE_TOKEN_AUDIENCES_PARAM);
     if (audiences != null) {
-      targetAudiences = audiences.split(",");
+      String[] auds = audiences.split(",");
+      for (int i = 0; i < auds.length; i++) {
+        targetAudiences.add(auds[i]);
+      }
     }
 
     String ttl = context.getInitParameter(SSO_COOKIE_TOKEN_TTL_PARAM);
@@ -180,14 +184,10 @@ public class WebSSOResource {
 
     try {
       JWT token = null;
-      if (targetAudiences == null || targetAudiences.length == 0) {
+      if (targetAudiences.isEmpty()) {
         token = ts.issueToken(p, "RS256", getExpiry());
       } else {
-        ArrayList<String> aud = new ArrayList<String>();
-        for (int i = 0; i < targetAudiences.length; i++) {
-          aud.add(targetAudiences[i]);
-        }
-        token = ts.issueToken(p, aud, "RS256", getExpiry());
+        token = ts.issueToken(p, targetAudiences, "RS256", getExpiry());
       }
 
       // Coverity CID 1327959

http://git-wip-us.apache.org/repos/asf/knox/blob/2666894b/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java b/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
index 73910dd..c953c91 100644
--- a/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
+++ b/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
@@ -17,15 +17,65 @@
  */
 package org.apache.hadoop.gateway.service.knoxsso;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.hadoop.gateway.services.GatewayServices;
+import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
+import org.apache.hadoop.gateway.services.security.token.TokenServiceException;
+import org.apache.hadoop.gateway.services.security.token.impl.JWT;
+import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
 import org.apache.hadoop.gateway.util.RegExUtils;
+import org.easymock.EasyMock;
 import org.junit.Assert;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+
 /**
- *
+ * Some tests for the Knox SSO service.
  */
 public class WebSSOResourceTest {
 
+  protected static RSAPublicKey publicKey;
+  protected static RSAPrivateKey privateKey;
+
+  @BeforeClass
+  public static void setup() throws Exception, NoSuchAlgorithmException {
+    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+    kpg.initialize(1024);
+    KeyPair KPair = kpg.generateKeyPair();
+
+    publicKey = (RSAPublicKey) KPair.getPublic();
+    privateKey = (RSAPrivateKey) KPair.getPrivate();
+  }
+
   @Test
   public void testWhitelistMatching() throws Exception {
     String whitelist = "^https?://.*example.com:8080/.*$;" +
@@ -35,37 +85,267 @@ public class WebSSOResourceTest {
         "^https?://localhost:\\d{0,9}/.*$;^/.*$";
 
     // match on explicit hostname/domain and port
-    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
         "http://host.example.com:8080/"));
     // match on non-required port
-    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
         "http://host.example.com/"));
     // match on required but any port
-    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
         "http://host.example2.com:1234/"));
     // fail on missing port
-    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
         "http://host.example2.com/"));
     // fail on invalid port
-    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
         "http://host.example.com:8081/"));
     // fail on alphanumeric port
-    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
         "http://host.example.com:A080/"));
     // fail on invalid hostname/domain
-    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
         "http://host.example.net:8080/"));
     // fail on required port
-    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
         "http://host.example2.com/"));
     // fail on required https
-    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
         "http://host.example3.com/"));
     // match on localhost and port
-    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
         "http://localhost:8080/"));
     // match on local/relative path
-    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist, 
+    Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
         "/local/resource/"));
   }
+
+  @Test
+  public void testGetToken() throws Exception {
+
+    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
+
+    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+    EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+
+    Principal principal = EasyMock.createNiceMock(Principal.class);
+    EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+    EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+    GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+    EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+    ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
+    CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
+
+    EasyMock.replay(principal, services, context, request);
+
+    WebSSOResource webSSOResponse = new WebSSOResource();
+    webSSOResponse.request = request;
+    webSSOResponse.response = responseWrapper;
+    webSSOResponse.context = context;
+    webSSOResponse.init();
+
+    // Issue a token
+    webSSOResponse.doGet();
+
+    // Check the cookie
+    Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
+    assertNotNull(cookie);
+
+    JWTToken parsedToken = new JWTToken(cookie.getValue());
+    assertEquals("alice", parsedToken.getSubject());
+    assertTrue(authority.verifyToken(parsedToken));
+  }
+
+  @Test
+  public void testAudiences() throws Exception {
+
+    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn("recipient1,recipient2");
+    EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
+
+    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+    EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+
+    Principal principal = EasyMock.createNiceMock(Principal.class);
+    EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+    EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+    GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+    EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+    ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
+    CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
+
+    EasyMock.replay(principal, services, context, request);
+
+    WebSSOResource webSSOResponse = new WebSSOResource();
+    webSSOResponse.request = request;
+    webSSOResponse.response = responseWrapper;
+    webSSOResponse.context = context;
+    webSSOResponse.init();
+
+    // Issue a token
+    webSSOResponse.doGet();
+
+    // Check the cookie
+    Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
+    assertNotNull(cookie);
+
+    JWTToken parsedToken = new JWTToken(cookie.getValue());
+    assertEquals("alice", parsedToken.getSubject());
+    assertTrue(authority.verifyToken(parsedToken));
+
+    // Verify the audiences
+    List<String> audiences = Arrays.asList(parsedToken.getAudienceClaims());
+    assertEquals(2, audiences.size());
+    assertTrue(audiences.contains("recipient1"));
+    assertTrue(audiences.contains("recipient2"));
+  }
+
+  /**
+   * A wrapper for HttpServletResponseWrapper to store the cookies
+   */
+  private static class CookieResponseWrapper extends HttpServletResponseWrapper {
+
+    private ServletOutputStream outputStream;
+    private Map<String, Cookie> cookies = new HashMap<>();
+
+    public CookieResponseWrapper(HttpServletResponse response) {
+        super(response);
+    }
+
+    public CookieResponseWrapper(HttpServletResponse response, ServletOutputStream outputStream) {
+        super(response);
+        this.outputStream = outputStream;
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() {
+        return outputStream;
+    }
+
+    @Override
+    public void addCookie(Cookie cookie) {
+        super.addCookie(cookie);
+        cookies.put(cookie.getName(), cookie);
+    }
+
+    public Cookie getCookie(String name) {
+        return cookies.get(name);
+    }
+
+  }
+
+  private static class TestJWTokenAuthority implements JWTokenAuthority {
+
+    private RSAPublicKey publicKey;
+    private RSAPrivateKey privateKey;
+
+    public TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
+      this.publicKey = publicKey;
+      this.privateKey = privateKey;
+    }
+
+    @Override
+    public JWTToken issueToken(Subject subject, String algorithm)
+      throws TokenServiceException {
+      Principal p = (Principal) subject.getPrincipals().toArray()[0];
+      return issueToken(p, algorithm);
+    }
+
+    @Override
+    public JWTToken issueToken(Principal p, String algorithm)
+      throws TokenServiceException {
+      return issueToken(p, null, algorithm);
+    }
+
+    @Override
+    public JWTToken issueToken(Principal p, String audience, String algorithm)
+      throws TokenServiceException {
+      return issueToken(p, audience, algorithm, -1);
+    }
+
+    @Override
+    public boolean verifyToken(JWTToken token) throws TokenServiceException {
+      JWSVerifier verifier = new RSASSAVerifier(publicKey);
+      return token.verify(verifier);
+    }
+
+    @Override
+    public JWTToken issueToken(Principal p, String audience, String algorithm,
+                               long expires) throws TokenServiceException {
+      List<String> audiences = null;
+      if (audience != null) {
+        audiences = new ArrayList<String>();
+        audiences.add(audience);
+      }
+      return issueToken(p, audiences, algorithm, expires);
+    }
+
+    @Override
+    public JWTToken issueToken(Principal p, List<String> audiences, String algorithm,
+                               long expires) throws TokenServiceException {
+      String[] claimArray = new String[4];
+      claimArray[0] = "KNOXSSO";
+      claimArray[1] = p.getName();
+      claimArray[2] = null;
+      if (expires == -1) {
+        claimArray[3] = null;
+      } else {
+        claimArray[3] = String.valueOf(expires);
+      }
+
+      JWTToken token = null;
+      if ("RS256".equals(algorithm)) {
+        token = new JWTToken("RS256", claimArray, audiences);
+        JWSSigner signer = new RSASSASigner(privateKey);
+        token.sign(signer);
+      } else {
+        throw new TokenServiceException("Cannot issue token - Unsupported algorithm");
+      }
+
+      return token;
+    }
+
+    @Override
+    public JWT issueToken(Principal p, String algorithm, long expiry)
+        throws TokenServiceException {
+      return issueToken(p, Collections.<String>emptyList(), algorithm, expiry);
+    }
+
+    @Override
+    public boolean verifyToken(JWTToken token, RSAPublicKey publicKey) throws TokenServiceException {
+      JWSVerifier verifier = new RSASSAVerifier(publicKey);
+      return token.verify(verifier);
+    }
+
+  }
+
 }