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

[12/12] knox git commit: Merge remote-tracking branch 'origin/master' into KNOX-998-Package_Restructuring

Merge remote-tracking branch 'origin/master' into KNOX-998-Package_Restructuring

# Conflicts:
#	gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
#	gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java


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

Branch: refs/heads/KNOX-998-Package_Restructuring
Commit: 416ee7c15076ddcaf25cd6a908e6cf1b39683673
Parents: f4a4355 2666894
Author: Sandeep More <mo...@apache.org>
Authored: Wed Sep 20 09:52:47 2017 -0400
Committer: Sandeep More <mo...@apache.org>
Committed: Wed Sep 20 09:52:47 2017 -0400

----------------------------------------------------------------------
 .../federation/AbstractJWTFilterTest.java       |  14 +-
 .../federation/JWTFederationFilterTest.java     |   1 -
 .../federation/SSOCookieProviderTest.java       |   1 -
 gateway-service-knoxsso/pom.xml                 |  11 +-
 .../gateway/service/knoxsso/WebSSOResource.java |  20 +-
 .../service/knoxsso/WebSSOResourceTest.java     | 303 +++++++++++++++-
 gateway-service-knoxtoken/pom.xml               |  29 +-
 .../service/knoxtoken/TokenResource.java        |  31 +-
 .../knoxtoken/TokenServiceResourceTest.java     | 257 +++++++++++++-
 .../gateway/AmbariServiceDefinitionTest.java    |  14 +-
 .../knox/gateway/GatewayAdminFuncTest.java      |   2 +-
 .../gateway/GatewayAdminTopologyFuncTest.java   |  62 ++--
 .../apache/knox/gateway/GatewayAppFuncTest.java |  70 ++--
 .../knox/gateway/GatewayBasicFuncTest.java      | 349 +++++++++----------
 .../knox/gateway/GatewayDeployFuncTest.java     |   2 +-
 .../knox/gateway/GatewayHealthFuncTest.java     |   6 +-
 .../GatewayLdapDynamicGroupFuncTest.java        |   6 +-
 .../knox/gateway/GatewayLdapGroupFuncTest.java  |   4 +-
 .../gateway/GatewayLdapPosixGroupFuncTest.java  |  13 +-
 .../gateway/GatewayLocalServiceFuncTest.java    |   2 +-
 .../knox/gateway/GatewayMultiFuncTest.java      |  26 +-
 .../GatewayPortMappingDisableFeatureTest.java   |   4 +-
 .../gateway/GatewayPortMappingFailTest.java     |   2 +-
 .../gateway/GatewayPortMappingFuncTest.java     |   4 +-
 .../knox/gateway/GatewaySampleFuncTest.java     |   2 +-
 .../apache/knox/gateway/Knox242FuncTest.java    |   6 +-
 .../apache/knox/gateway/WebHdfsHaFuncTest.java  |  30 +-
 27 files changed, 895 insertions(+), 376 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
index 10efeb5,0000000..ea56486
mode 100644,000000..100644
--- a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
@@@ -1,636 -1,0 +1,636 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.provider.federation;
 +
 +import static org.junit.Assert.fail;
 +
 +import java.io.IOException;
 +import java.net.InetAddress;
 +import java.security.AccessController;
 +import java.security.KeyPair;
 +import java.security.KeyPairGenerator;
 +import java.security.NoSuchAlgorithmException;
 +import java.security.Principal;
 +import java.security.PublicKey;
 +import java.security.cert.Certificate;
 +import java.security.interfaces.RSAPrivateKey;
 +import java.security.interfaces.RSAPublicKey;
 +import java.text.MessageFormat;
 +import java.util.Enumeration;
 +import java.util.List;
 +import java.util.ArrayList;
 +import java.util.Properties;
 +import java.util.Date;
 +import java.util.Set;
 +
 +import javax.security.auth.Subject;
 +import javax.servlet.FilterChain;
 +import javax.servlet.FilterConfig;
 +import javax.servlet.ServletContext;
 +import javax.servlet.ServletException;
 +import javax.servlet.ServletRequest;
 +import javax.servlet.ServletResponse;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +
 +import org.apache.commons.codec.binary.Base64;
 +import org.apache.knox.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
 +import org.apache.knox.gateway.provider.federation.jwt.filter.SSOCookieFederationFilter;
 +import org.apache.knox.gateway.security.PrimaryPrincipal;
 +import org.apache.knox.gateway.services.security.impl.X509CertificateUtil;
 +import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 +import org.apache.knox.gateway.services.security.token.TokenServiceException;
 +import org.apache.knox.gateway.services.security.token.impl.JWT;
 +import org.apache.knox.gateway.services.security.token.impl.JWTToken;
 +import org.easymock.EasyMock;
 +import org.junit.After;
 +import org.junit.Assert;
- import org.junit.Before;
++import org.junit.BeforeClass;
 +import org.junit.Test;
 +
 +import com.nimbusds.jose.*;
 +import com.nimbusds.jwt.JWTClaimsSet;
 +import com.nimbusds.jwt.SignedJWT;
 +import com.nimbusds.jose.crypto.RSASSASigner;
 +import com.nimbusds.jose.crypto.RSASSAVerifier;
 +
 +public abstract class AbstractJWTFilterTest  {
 +  private static final String SERVICE_URL = "https://localhost:8888/resource";
 +  private static final String dnTemplate = "CN={0},OU=Test,O=Hadoop,L=Test,ST=Test,C=US";
 +
 +  protected AbstractJWTFilter handler = null;
-   protected RSAPublicKey publicKey = null;
-   protected RSAPrivateKey privateKey = null;
-   protected String pem = null;
++  protected static RSAPublicKey publicKey = null;
++  protected static RSAPrivateKey privateKey = null;
++  protected static String pem = null;
 +
 +  protected abstract void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt);
 +  protected abstract void setGarbledTokenOnRequest(HttpServletRequest request, SignedJWT jwt);
 +  protected abstract String getAudienceProperty();
 +  protected abstract String getVerificationPemProperty();
 +
-   private String buildDistinguishedName(String hostname) {
++  private static String buildDistinguishedName(String hostname) {
 +    MessageFormat headerFormatter = new MessageFormat(dnTemplate);
 +    String[] paramArray = new String[1];
 +    paramArray[0] = hostname;
 +    String dn = headerFormatter.format(paramArray);
 +    return dn;
 +  }
 +
-   @Before
-   public void setup() throws Exception, NoSuchAlgorithmException {
++  @BeforeClass
++  public static void generateKeys() throws Exception, NoSuchAlgorithmException {
 +    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
 +    kpg.initialize(2048);
 +    KeyPair KPair = kpg.generateKeyPair();
 +    String dn = buildDistinguishedName(InetAddress.getLocalHost().getHostName());
 +    Certificate cert = X509CertificateUtil.generateCertificate(dn, KPair, 365, "SHA1withRSA");
 +    byte[] data = cert.getEncoded();
 +    Base64 encoder = new Base64( 76, "\n".getBytes( "ASCII" ) );
 +    pem = new String(encoder.encodeToString( data ).getBytes( "ASCII" )).trim();
 +
 +    publicKey = (RSAPublicKey) KPair.getPublic();
 +    privateKey = (RSAPrivateKey) KPair.getPrivate();
 +  }
 +
 +  @After
 +  public void teardown() throws Exception {
 +    handler.destroy();
 +  }
 +
 +  @Test
 +  public void testValidJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
 +      Assert.assertEquals("Not the expected principal", "alice", ((Principal)principals.toArray()[0]).getName());
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testValidAudienceJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      props.put(getAudienceProperty(), "bar");
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
 +      Assert.assertEquals("Not the expected principal", "alice", ((Principal)principals.toArray()[0]).getName());
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testInvalidAudienceJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      props.put(getAudienceProperty(), "foo");
 +      props.put("sso.authentication.provider.url", "https://localhost:8443/gateway/knoxsso/api/v1/websso");
 +
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testValidVerificationPEM() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +
 +//      System.out.println("+" + pem + "+");
 +
 +      props.put(getAudienceProperty(), "bar");
 +      props.put("sso.authentication.provider.url", "https://localhost:8443/gateway/knoxsso/api/v1/websso");
 +      props.put(getVerificationPemProperty(), pem);
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 50000), privateKey, props);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
 +      Assert.assertEquals("Not the expected principal", "alice", ((Principal)principals.toArray()[0]).getName());
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testExpiredJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() - 1000), privateKey, props);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", !chain.doFilterCalled);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testValidJWTNoExpiration() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("alice", null, privateKey, props);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL).anyTimes();
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
 +      Assert.assertEquals("Not the expected principal", "alice", ((Principal)principals.toArray()[0]).getName());
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testUnableToParseJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("bob", new Date(new Date().getTime() + 5000), privateKey, props);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setGarbledTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL).anyTimes();
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testFailedSignatureValidationJWT() throws Exception {
 +    try {
 +      // Create a private key to sign the token
 +      KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
 +      kpg.initialize(1024);
 +
 +      KeyPair kp = kpg.genKeyPair();
 +
 +      Properties props = getProperties();
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("bob", new Date(new Date().getTime() + 5000),
 +                             (RSAPrivateKey)kp.getPrivate(), props);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL).anyTimes();
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testInvalidVerificationPEM() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +
 +      KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
 +      kpg.initialize(1024);
 +
 +      KeyPair KPair = kpg.generateKeyPair();
 +      String dn = buildDistinguishedName(InetAddress.getLocalHost().getHostName());
 +      Certificate cert = X509CertificateUtil.generateCertificate(dn, KPair, 365, "SHA1withRSA");
 +      byte[] data = cert.getEncoded();
 +      Base64 encoder = new Base64( 76, "\n".getBytes( "ASCII" ) );
 +      String failingPem = new String(encoder.encodeToString( data ).getBytes( "ASCII" )).trim();
 +
 +      props.put(getAudienceProperty(), "bar");
 +      props.put(getVerificationPemProperty(), failingPem);
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 50000), privateKey, props);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", chain.doFilterCalled == false);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testInvalidIssuer() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("new-issuer", "alice", new Date(new Date().getTime() + 5000), privateKey);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +         new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testValidIssuerViaConfig() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      props.setProperty(AbstractJWTFilter.JWT_EXPECTED_ISSUER, "new-issuer");
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("new-issuer", "alice", new Date(new Date().getTime() + 5000), privateKey);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled);
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal", principals.size() > 0);
 +      Assert.assertEquals("Not the expected principal", "alice", ((Principal)principals.toArray()[0]).getName());
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  protected Properties getProperties() {
 +    Properties props = new Properties();
 +    props.setProperty(
 +        SSOCookieFederationFilter.SSO_AUTHENTICATION_PROVIDER_URL,
 +        "https://localhost:8443/authserver");
 +    return props;
 +  }
 +
 +  protected SignedJWT getJWT(String sub, Date expires, RSAPrivateKey privateKey,
 +      Properties props) throws Exception {
 +    return getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, sub, expires, privateKey);
 +  }
 +
 +  protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey)
 +      throws Exception {
 +    List<String> aud = new ArrayList<String>();
 +    aud.add("bar");
 +
 +    JWTClaimsSet claims = new JWTClaimsSet.Builder()
 +    .issuer(issuer)
 +    .subject(sub)
 +    .audience(aud)
 +    .expirationTime(expires)
 +    .claim("scope", "openid")
 +    .build();
 +
 +    JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).build();
 +
 +    SignedJWT signedJWT = new SignedJWT(header, claims);
 +    JWSSigner signer = new RSASSASigner(privateKey);
 +
 +    signedJWT.sign(signer);
 +
 +    return signedJWT;
 +  }
 +
 +  protected static class TestFilterConfig implements FilterConfig {
 +    Properties props = null;
 +
 +    public TestFilterConfig(Properties props) {
 +      this.props = props;
 +    }
 +
 +    @Override
 +    public String getFilterName() {
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see javax.servlet.FilterConfig#getServletContext()
 +     */
 +    @Override
 +    public ServletContext getServletContext() {
 +//      JWTokenAuthority authority = EasyMock.createNiceMock(JWTokenAuthority.class);
 +//      GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
 +//      EasyMock.expect(services.getService("TokenService").andReturn(authority));
 +//      ServletContext context = EasyMock.createNiceMock(ServletContext.class);
 +//      EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE).andReturn(new DefaultGatewayServices()));
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see javax.servlet.FilterConfig#getInitParameter(java.lang.String)
 +     */
 +    @Override
 +    public String getInitParameter(String name) {
 +      return props.getProperty(name, null);
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see javax.servlet.FilterConfig#getInitParameterNames()
 +     */
 +    @Override
 +    public Enumeration<String> getInitParameterNames() {
 +      return null;
 +    }
 +
 +  }
 +
 +  protected static class TestJWTokenAuthority implements JWTokenAuthority {
 +
 +    private PublicKey verifyingKey;
 +
 +    public TestJWTokenAuthority(PublicKey verifyingKey) {
 +      this.verifyingKey = verifyingKey;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(javax.security.auth.Subject, java.lang.String)
 +     */
 +    @Override
 +    public JWTToken issueToken(Subject subject, String algorithm)
 +        throws TokenServiceException {
 +      // TODO Auto-generated method stub
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String)
 +     */
 +    @Override
 +    public JWTToken issueToken(Principal p, String algorithm)
 +        throws TokenServiceException {
 +      // TODO Auto-generated method stub
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, java.lang.String)
 +     */
 +    @Override
 +    public JWTToken issueToken(Principal p, String audience, String algorithm)
 +        throws TokenServiceException {
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#verifyToken(JWTToken)
 +     */
 +    @Override
 +    public boolean verifyToken(JWTToken token) throws TokenServiceException {
 +      JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) verifyingKey);
 +      return token.verify(verifier);
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, java.lang.String, long)
 +     */
 +    @Override
 +    public JWTToken issueToken(Principal p, String audience, String algorithm,
 +        long expires) throws TokenServiceException {
 +      return null;
 +    }
 +
 +    @Override
 +    public JWTToken issueToken(Principal p, List<String> audiences, String algorithm,
 +        long expires) throws TokenServiceException {
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, long)
 +     */
 +    @Override
 +    public JWT issueToken(Principal p, String audience, long l)
 +        throws TokenServiceException {
 +      // TODO Auto-generated method stub
 +      return null;
 +    }
 +
 +    @Override
 +    public boolean verifyToken(JWTToken token, RSAPublicKey publicKey) throws TokenServiceException {
 +      JWSVerifier verifier = new RSASSAVerifier(publicKey);
 +      return token.verify(verifier);
 +    }
 +
 +  }
 +
 +  protected static class TestFilterChain implements FilterChain {
 +    boolean doFilterCalled = false;
 +    Subject subject = null;
 +
 +    /* (non-Javadoc)
 +     * @see javax.servlet.FilterChain#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
 +     */
 +    @Override
 +    public void doFilter(ServletRequest request, ServletResponse response)
 +        throws IOException, ServletException {
 +      doFilterCalled = true;
 +
 +      subject = Subject.getSubject( AccessController.getContext() );
 +    }
 +
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java
index c35d013,0000000..bfb5e91
mode 100644,000000..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
@@@ -1,67 -1,0 +1,66 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.provider.federation;
 +
 +import java.security.NoSuchAlgorithmException;
 +
 +import javax.servlet.http.HttpServletRequest;
 +
 +import org.apache.knox.gateway.provider.federation.jwt.filter.JWTFederationFilter;
 +import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 +import org.easymock.EasyMock;
 +import org.junit.Before;
 +
 +import com.nimbusds.jwt.SignedJWT;
 +
 +public class JWTFederationFilterTest extends AbstractJWTFilterTest {
 +
 +    @Before
 +    public void setup() throws Exception, NoSuchAlgorithmException {
-       super.setup();
 +      handler = new TestJWTFederationFilter();
 +      ((TestJWTFederationFilter) handler).setTokenService(new TestJWTokenAuthority(publicKey));
 +    }
 +
 +    protected void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt) {
 +      String token = "Bearer " + jwt.serialize();
 +      EasyMock.expect(request.getHeader("Authorization")).andReturn(token);
 +    }
 +
 +    protected void setGarbledTokenOnRequest(HttpServletRequest request, SignedJWT jwt) {
 +      String token = "Bearer " + "ljm" + jwt.serialize();
 +      EasyMock.expect(request.getHeader("Authorization")).andReturn(token);
 +    }
 +
 +    protected String getAudienceProperty() {
 +      return TestJWTFederationFilter.KNOX_TOKEN_AUDIENCES;
 +    }
 +
 +    private static class TestJWTFederationFilter extends JWTFederationFilter {
 +
 +      public void setTokenService(JWTokenAuthority ts) {
 +        authority = ts;
 +      }
 +
 +    }
 +
 +    @Override
 +    protected String getVerificationPemProperty() {
 +      return TestJWTFederationFilter.TOKEN_VERIFICATION_PEM;
 +    };
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
index d217799,0000000..babbee2
mode 100644,000000..100644
--- a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
@@@ -1,162 -1,0 +1,161 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.provider.federation;
 +
 +import static org.junit.Assert.fail;
 +
 +import java.security.NoSuchAlgorithmException;
 +import java.security.Principal;
 +import java.util.Properties;
 +import java.util.Date;
 +import java.util.Set;
 +
 +import javax.servlet.ServletException;
 +import javax.servlet.http.Cookie;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +
 +import org.apache.knox.gateway.provider.federation.jwt.filter.SSOCookieFederationFilter;
 +import org.apache.knox.gateway.security.PrimaryPrincipal;
 +import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 +import org.easymock.EasyMock;
 +import org.junit.Assert;
 +import org.junit.Before;
 +import org.junit.Test;
 +
 +import com.nimbusds.jwt.SignedJWT;
 +
 +public class SSOCookieProviderTest extends AbstractJWTFilterTest {
 +  private static final String SERVICE_URL = "https://localhost:8888/resource";
 +
 +  @Before
 +  public void setup() throws Exception, NoSuchAlgorithmException {
-     super.setup();
 +    handler = new TestSSOCookieFederationProvider();
 +    ((TestSSOCookieFederationProvider) handler).setTokenService(new TestJWTokenAuthority(publicKey));
 +  }
 +
 +  protected void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt) {
 +    Cookie cookie = new Cookie("hadoop-jwt", jwt.serialize());
 +    EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie });
 +  }
 +
 +  protected void setGarbledTokenOnRequest(HttpServletRequest request, SignedJWT jwt) {
 +    Cookie cookie = new Cookie("hadoop-jwt", "ljm" + jwt.serialize());
 +    EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie });
 +  }
 +
 +  protected String getAudienceProperty() {
 +    return TestSSOCookieFederationProvider.SSO_EXPECTED_AUDIENCES;
 +  }
 +
 +  @Test
 +  public void testCustomCookieNameJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      props.put("sso.cookie.name", "jowt");
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000),
 +          privateKey, props);
 +
 +      Cookie cookie = new Cookie("jowt", jwt.serialize());
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie });
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal returned.", !principals.isEmpty());
 +      Assert.assertEquals("Not the expected principal", "alice", ((Principal)principals.toArray()[0]).getName());
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testNoProviderURLJWT() throws Exception {
 +    try {
 +      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");
 +    }
 +  }
 +
 +  @Test
 +  public void testOrigURLWithQueryString() throws Exception {
 +    Properties props = getProperties();
 +    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("name=value");
 +    EasyMock.replay(request);
 +
 +    String loginURL = ((TestSSOCookieFederationProvider)handler).testConstructLoginURL(request);
 +    Assert.assertNotNull("loginURL should not be null.", loginURL);
 +    Assert.assertEquals("https://localhost:8443/authserver?originalUrl=" + SERVICE_URL + "?name=value", loginURL);
 +  }
 +
 +  @Test
 +  public void testOrigURLNoQueryString() throws Exception {
 +    Properties props = getProperties();
 +    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.replay(request);
 +
 +    String loginURL = ((TestSSOCookieFederationProvider)handler).testConstructLoginURL(request);
 +    Assert.assertNotNull("LoginURL should not be null.", loginURL);
 +    Assert.assertEquals("https://localhost:8443/authserver?originalUrl=" + SERVICE_URL, loginURL);
 +  }
 +
 +
 +  @Override
 +  protected String getVerificationPemProperty() {
 +    return SSOCookieFederationFilter.SSO_VERIFICATION_PEM;
 +  };
 +
 +  private static class TestSSOCookieFederationProvider extends SSOCookieFederationFilter {
 +    public String testConstructLoginURL(HttpServletRequest req) {
 +      return constructLoginURL(req);
 +    }
 +
 +    public void setTokenService(JWTokenAuthority ts) {
 +      authority = ts;
 +    }
 +  };
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
----------------------------------------------------------------------
diff --cc gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
index a6bb3f7,0000000..8a9d028
mode 100644,000000..100644
--- a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
+++ b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
@@@ -1,322 -1,0 +1,322 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.service.knoxsso;
 +
 +import java.io.IOException;
 +import java.net.URI;
 +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;
 +
 +import javax.annotation.PostConstruct;
 +import javax.servlet.ServletContext;
 +import javax.servlet.http.Cookie;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +import javax.servlet.http.HttpSession;
 +import javax.ws.rs.GET;
 +import javax.ws.rs.POST;
 +import javax.ws.rs.Path;
 +import javax.ws.rs.Produces;
 +import javax.ws.rs.core.Context;
 +import javax.ws.rs.core.Response;
 +import javax.ws.rs.WebApplicationException;
 +
 +import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 +import org.apache.knox.gateway.services.GatewayServices;
 +import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 +import org.apache.knox.gateway.services.security.token.TokenServiceException;
 +import org.apache.knox.gateway.services.security.token.impl.JWT;
 +import org.apache.knox.gateway.util.RegExUtils;
 +import org.apache.knox.gateway.util.Urls;
 +
 +import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 +import static javax.ws.rs.core.MediaType.APPLICATION_XML;
 +
 +@Path( WebSSOResource.RESOURCE_PATH )
 +public class WebSSOResource {
 +  private static final String SSO_COOKIE_NAME = "knoxsso.cookie.name";
 +  private static final String SSO_COOKIE_SECURE_ONLY_INIT_PARAM = "knoxsso.cookie.secure.only";
 +  private static final String SSO_COOKIE_MAX_AGE_INIT_PARAM = "knoxsso.cookie.max.age";
 +  private static final String SSO_COOKIE_DOMAIN_SUFFIX_PARAM = "knoxsso.cookie.domain.suffix";
 +  private static final String SSO_COOKIE_TOKEN_TTL_PARAM = "knoxsso.token.ttl";
 +  private static final String SSO_COOKIE_TOKEN_AUDIENCES_PARAM = "knoxsso.token.audiences";
 +  private static final String SSO_COOKIE_TOKEN_WHITELIST_PARAM = "knoxsso.redirect.whitelist.regex";
 +  private static final String SSO_ENABLE_SESSION_PARAM = "knoxsso.enable.session";
 +  private static final String ORIGINAL_URL_REQUEST_PARAM = "originalUrl";
 +  private static final String ORIGINAL_URL_COOKIE_NAME = "original-url";
 +  private static final String DEFAULT_SSO_COOKIE_NAME = "hadoop-jwt";
 +  // default for the whitelist - open up for development - relative paths and localhost only
 +  private static final String DEFAULT_WHITELIST = "^/.*$;^https?://(localhost|127.0.0.1|0:0:0:0:0:0:0:1|::1):\\d{0,9}/.*$";
 +  static final String RESOURCE_PATH = "/api/v1/websso";
 +  private static KnoxSSOMessages log = MessagesFactory.get( KnoxSSOMessages.class );
 +  private String cookieName = null;
 +  private boolean secureOnly = true;
 +  private int maxAge = -1;
 +  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;
 +
 +  @PostConstruct
 +  public void init() {
 +
 +    // configured cookieName
 +    cookieName = context.getInitParameter(SSO_COOKIE_NAME);
 +    if (cookieName == null) {
 +      cookieName = DEFAULT_SSO_COOKIE_NAME;
 +    }
 +
 +    String secure = context.getInitParameter(SSO_COOKIE_SECURE_ONLY_INIT_PARAM);
 +    if (secure != null) {
 +      secureOnly = ("false".equals(secure) ? false : true);
 +      if (!secureOnly) {
 +        log.cookieSecureOnly(secureOnly);
 +      }
 +    }
 +
 +    String age = context.getInitParameter(SSO_COOKIE_MAX_AGE_INIT_PARAM);
 +    if (age != null) {
 +      try {
 +        log.setMaxAge(age);
 +        maxAge = Integer.parseInt(age);
 +      }
 +      catch (NumberFormatException nfe) {
 +        log.invalidMaxAgeEncountered(age);
 +      }
 +    }
 +
 +    domainSuffix = context.getInitParameter(SSO_COOKIE_DOMAIN_SUFFIX_PARAM);
 +
 +    whitelist = context.getInitParameter(SSO_COOKIE_TOKEN_WHITELIST_PARAM);
 +    if (whitelist == null) {
 +      // default to local/relative targets
 +      whitelist = DEFAULT_WHITELIST;
 +    }
 +
 +    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);
 +    if (ttl != null) {
 +      try {
 +        tokenTTL = Long.parseLong(ttl);
 +      }
 +      catch (NumberFormatException nfe) {
 +        log.invalidTokenTTLEncountered(ttl);
 +      }
 +    }
 +
 +    String enableSession = context.getInitParameter(SSO_ENABLE_SESSION_PARAM);
 +    this.enableSession = ("true".equals(enableSession));
 +  }
 +
 +  @GET
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  public Response doGet() {
 +    return getAuthenticationToken(HttpServletResponse.SC_TEMPORARY_REDIRECT);
 +  }
 +
 +  @POST
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  public Response doPost() {
 +    return getAuthenticationToken(HttpServletResponse.SC_SEE_OTHER);
 +  }
 +
 +  private Response getAuthenticationToken(int statusCode) {
 +    GatewayServices services = (GatewayServices) request.getServletContext()
 +            .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +    boolean removeOriginalUrlCookie = true;
 +    String original = getCookieValue((HttpServletRequest) request, ORIGINAL_URL_COOKIE_NAME);
 +    if (original == null) {
 +      // in the case where there are no SAML redirects done before here
 +      // we need to get it from the request parameters
 +      removeOriginalUrlCookie = false;
 +      original = getOriginalUrlFromQueryParams();
 +      if (original.isEmpty()) {
 +        log.originalURLNotFound();
 +        throw new WebApplicationException("Original URL not found in the request.", Response.Status.BAD_REQUEST);
 +      }
 +      boolean validRedirect = RegExUtils.checkWhitelist(whitelist, original);
 +      if (!validRedirect) {
 +        log.whiteListMatchFail(original, whitelist);
 +        throw new WebApplicationException("Original URL not valid according to the configured whitelist.",
 +                Response.Status.BAD_REQUEST);
 +      }
 +    }
 +
 +    JWTokenAuthority ts = services.getService(GatewayServices.TOKEN_SERVICE);
 +    Principal p = ((HttpServletRequest)request).getUserPrincipal();
 +
 +    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
 +      if( token != null ) {
 +        addJWTHadoopCookie( original, token );
 +      }
 +
 +      if (removeOriginalUrlCookie) {
 +        removeOriginalUrlCookie(response);
 +      }
 +
 +      log.aboutToRedirectToOriginal(original);
 +      response.setStatus(statusCode);
 +      response.setHeader("Location", original);
 +      try {
 +        response.getOutputStream().close();
 +      } catch (IOException e) {
 +        log.unableToCloseOutputStream(e.getMessage(), Arrays.toString(e.getStackTrace()));
 +      }
 +    }
 +    catch (TokenServiceException e) {
 +      log.unableToIssueToken(e);
 +    }
 +    URI location = null;
 +    try {
 +      location = new URI(original);
 +    }
 +    catch(URISyntaxException urise) {
 +      // todo log return error response
 +    }
 +
 +    if (!enableSession) {
 +      // invalidate the session to avoid autologin
 +      // Coverity CID 1352857
 +      HttpSession session = request.getSession(false);
 +      if( session != null ) {
 +        session.invalidate();
 +      }
 +    }
 +
 +    return Response.seeOther(location).entity("{ \"redirectTo\" : " + original + " }").build();
 +  }
 +
 +  private String getOriginalUrlFromQueryParams() {
 +    String original = request.getParameter(ORIGINAL_URL_REQUEST_PARAM);
 +    StringBuffer buf = new StringBuffer(original);
 +
 +    // Add any other query params.
 +    // Probably not ideal but will not break existing integrations by requiring
 +    // some encoding.
 +    Map<String, String[]> params = request.getParameterMap();
 +    for (Entry<String, String[]> entry : params.entrySet()) {
 +      if (!ORIGINAL_URL_REQUEST_PARAM.equals(entry.getKey())
 +          && !original.contains(entry.getKey() + "=")) {
 +        buf.append("&").append(entry.getKey());
 +        String[] values = entry.getValue();
 +        if (values.length > 0 && values[0] != null) {
 +          buf.append("=");
 +        }
 +        for (int i = 0; i < values.length; i++) {
 +          if (values[0] != null) {
 +            buf.append(values[i]);
 +            if (i < values.length-1) {
 +              buf.append("&").append(entry.getKey()).append("=");
 +            }
 +          }
 +        }
 +      }
 +    }
 +
 +    return buf.toString();
 +  }
 +
 +  private long getExpiry() {
 +    long expiry = 0l;
 +    if (tokenTTL == -1) {
 +      expiry = -1;
 +    }
 +    else {
 +      expiry = System.currentTimeMillis() + tokenTTL;
 +    }
 +    return expiry;
 +  }
 +
 +  private void addJWTHadoopCookie(String original, JWT token) {
 +    log.addingJWTCookie(token.toString());
 +    Cookie c = new Cookie(cookieName,  token.toString());
 +    c.setPath("/");
 +    try {
 +      String domain = Urls.getDomainName(original, domainSuffix);
 +      if (domain != null) {
 +        c.setDomain(domain);
 +      }
 +      c.setHttpOnly(true);
 +      if (secureOnly) {
 +        c.setSecure(true);
 +      }
 +      if (maxAge != -1) {
 +        c.setMaxAge(maxAge);
 +      }
 +      response.addCookie(c);
 +      log.addedJWTCookie();
 +    }
 +    catch(Exception e) {
 +      log.unableAddCookieToResponse(e.getMessage(), Arrays.toString(e.getStackTrace()));
 +      throw new WebApplicationException("Unable to add JWT cookie to response.");
 +    }
 +  }
 +
 +  private void removeOriginalUrlCookie(HttpServletResponse response) {
 +    Cookie c = new Cookie(ORIGINAL_URL_COOKIE_NAME, null);
 +    c.setMaxAge(0);
 +    c.setPath(RESOURCE_PATH);
 +    response.addCookie(c);
 +  }
 +
 +  private String getCookieValue(HttpServletRequest request, String name) {
 +    Cookie[] cookies = request.getCookies();
 +    String value = null;
 +    if (cookies != null) {
 +      for(Cookie cookie : cookies){
 +        if(name.equals(cookie.getName())){
 +          value = cookie.getValue();
 +        }
 +      }
 +    }
 +    if (value == null) {
 +      log.cookieNotFound(name);
 +    }
 +    return value;
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
----------------------------------------------------------------------
diff --cc gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
index 5b195e4,0000000..864440c
mode 100644,000000..100644
--- a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
+++ b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
@@@ -1,71 -1,0 +1,352 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.service.knoxsso;
 +
 +import org.apache.knox.gateway.util.RegExUtils;
++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.knox.gateway.services.GatewayServices;
++import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
++import org.apache.knox.gateway.services.security.token.TokenServiceException;
++import org.apache.knox.gateway.services.security.token.impl.JWT;
++import org.apache.knox.gateway.services.security.token.impl.JWTToken;
++import org.apache.knox.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/.*$;" +
 +        "^https?://.*example.com/.*$;" +
 +        "^https?://.*example2.com:\\d{0,9}/.*$;" +
 +        "^https://.*example3.com:\\d{0,9}/.*$;" +
 +        "^https?://localhost:\\d{0,9}/.*$;^/.*$";
 +
 +    // match on explicit hostname/domain and port
 +    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);
++    }
++
++  }
++
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
----------------------------------------------------------------------
diff --cc gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
index 9118d50,0000000..2c77bdf
mode 100644,000000..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
@@@ -1,182 -1,0 +1,183 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.service.knoxtoken;
 +
 +import java.io.IOException;
 +import java.security.Principal;
 +import java.util.ArrayList;
 +import java.util.Map;
 +import java.util.HashMap;
++import java.util.List;
++
 +import javax.annotation.PostConstruct;
 +import javax.servlet.ServletContext;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +import javax.ws.rs.GET;
 +import javax.ws.rs.POST;
 +import javax.ws.rs.Path;
 +import javax.ws.rs.Produces;
 +import javax.ws.rs.core.Context;
 +import javax.ws.rs.core.Response;
 +import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 +import org.apache.knox.gateway.services.GatewayServices;
 +import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 +import org.apache.knox.gateway.services.security.token.TokenServiceException;
 +import org.apache.knox.gateway.services.security.token.impl.JWT;
 +import org.apache.knox.gateway.util.JsonUtils;
 +
 +import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 +import static javax.ws.rs.core.MediaType.APPLICATION_XML;
 +
 +@Path( TokenResource.RESOURCE_PATH )
 +public class TokenResource {
 +  private static final String EXPIRES_IN = "expires_in";
 +  private static final String TOKEN_TYPE = "token_type";
 +  private static final String ACCESS_TOKEN = "access_token";
 +  private static final String TARGET_URL = "target_url";
 +  private static final String BEARER = "Bearer ";
 +  private static final String TOKEN_TTL_PARAM = "knox.token.ttl";
 +  private static final String TOKEN_AUDIENCES_PARAM = "knox.token.audiences";
 +  private static final String TOKEN_TARGET_URL = "knox.token.target.url";
 +  private static final String TOKEN_CLIENT_DATA = "knox.token.client.data";
 +  static final String RESOURCE_PATH = "knoxtoken/api/v1/token";
 +  private static TokenServiceMessages log = MessagesFactory.get( TokenServiceMessages.class );
 +  private long tokenTTL = 30000l;
-   private String[] targetAudiences = null;
++  private List<String> targetAudiences = new ArrayList<>();
 +  private String tokenTargetUrl = null;
 +  private Map<String,Object> tokenClientDataMap = null;
 +
 +  @Context
-   private HttpServletRequest request;
++  HttpServletRequest request;
 +
 +  @Context
-   private HttpServletResponse response;
++  HttpServletResponse response;
 +
 +  @Context
 +  ServletContext context;
 +
 +  @PostConstruct
 +  public void init() {
 +
 +    String audiences = context.getInitParameter(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(TOKEN_TTL_PARAM);
 +    if (ttl != null) {
 +      try {
 +        tokenTTL = Long.parseLong(ttl);
 +      }
 +      catch (NumberFormatException nfe) {
 +        log.invalidTokenTTLEncountered(ttl);
 +      }
 +    }
-     
++
 +    tokenTargetUrl = context.getInitParameter(TOKEN_TARGET_URL);
 +
 +    String clientData = context.getInitParameter(TOKEN_CLIENT_DATA);
 +    if (clientData != null) {
 +      tokenClientDataMap = new HashMap<>();
 +      String[] tokenClientData = clientData.split(",");
 +      addClientDataToMap(tokenClientData, tokenClientDataMap);
 +    }
 +  }
 +
 +  @GET
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  public Response doGet() {
 +    return getAuthenticationToken();
 +  }
 +
 +  @POST
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  public Response doPost() {
 +    return getAuthenticationToken();
 +  }
 +
 +  private Response getAuthenticationToken() {
 +    GatewayServices services = (GatewayServices) request.getServletContext()
 +            .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +    JWTokenAuthority ts = services.getService(GatewayServices.TOKEN_SERVICE);
 +    Principal p = ((HttpServletRequest)request).getUserPrincipal();
 +    long expires = getExpiry();
-     
++
 +    try {
 +      JWT token = null;
-       if (targetAudiences == null || targetAudiences.length == 0) {
-         token = ts.issueToken(p, "RS256", getExpiry());
++      if (targetAudiences.isEmpty()) {
++        token = ts.issueToken(p, "RS256", expires);
 +      } 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", expires);
++        token = ts.issueToken(p, targetAudiences, "RS256", expires);
 +      }
 +
 +      if (token != null) {
 +        String accessToken = token.toString();
-   
++
 +        HashMap<String, Object> map = new HashMap<>();
 +        map.put(ACCESS_TOKEN, accessToken);
 +        map.put(TOKEN_TYPE, BEARER);
 +        map.put(EXPIRES_IN, expires);
 +        if (tokenTargetUrl != null) {
 +          map.put(TARGET_URL, tokenTargetUrl);
 +        }
 +        if (tokenClientDataMap != null) {
 +          map.putAll(tokenClientDataMap);
 +        }
-   
++
 +        String jsonResponse = JsonUtils.renderAsJsonString(map);
 +
 +        response.getWriter().write(jsonResponse);
 +        return Response.ok().build();
 +      }
 +      else {
 +        return Response.serverError().build();
 +      }
 +    }
 +    catch (TokenServiceException | IOException e) {
 +      log.unableToIssueToken(e);
 +    }
 +    return Response.ok().entity("{ \"Unable to acquire token.\" }").build();
 +  }
 +
 +  void addClientDataToMap(String[] tokenClientData,
 +      Map<String,Object> map) {
 +    String[] kv = null;
 +    for (int i = 0; i < tokenClientData.length; i++) {
 +      kv = tokenClientData[i].split("=");
 +      if (kv.length == 2) {
 +        map.put(kv[0], kv[1]);
 +      }
 +    }
 +  }
 +
 +  private long getExpiry() {
 +    long expiry = 0l;
 +    if (tokenTTL == -1) {
 +      expiry = -1;
 +    }
 +    else {
 +      expiry = System.currentTimeMillis() + tokenTTL;
 +    }
 +    return expiry;
 +  }
 +}