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 2018/01/11 17:38:38 UTC

[11/53] [abbrv] knox git commit: Merge branch 'master' into KNOX-998-Package_Restructuring

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/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 6b8411e,0000000..0eb717e
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,410 -1,0 +1,689 @@@
 +/**
 + * 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.Date;
 +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,
 +        "http://host.example.com/"));
 +    // match on required but any port
 +    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,
 +        "http://host.example2.com/"));
 +    // fail on invalid port
 +    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,
 +        "http://host.example.com:A080/"));
 +    // fail on invalid hostname/domain
 +    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,
 +        "http://host.example2.com/"));
 +    // fail on required https
 +    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,
 +        "http://localhost:8080/"));
 +    // match on local/relative path
 +    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());
++    JWT 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());
++    JWT 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"));
 +  }
 +
 +  @Test
 +  public void testAudiencesWhitespace() 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"));
 +  }
 +
++  @Test
++  public void testSignatureAlgorithm() 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);
++    EasyMock.expect(context.getInitParameter("knoxsso.token.sigalg")).andReturn("RS512");
++
++    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);
++
++    JWT parsedToken = new JWTToken(cookie.getValue());
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++    assertTrue(parsedToken.getHeader().contains("RS512"));
++  }
++
++  @Test
++  public void testDefaultTTL() 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);
++
++    JWT parsedToken = new JWTToken(cookie.getValue());
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++
++    Date expiresDate = parsedToken.getExpiresDate();
++    Date now = new Date();
++    assertTrue(expiresDate.after(now));
++    assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++  }
++
++  @Test
++  public void testCustomTTL() 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("60000");
++    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);
++
++    JWT parsedToken = new JWTToken(cookie.getValue());
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++
++    Date expiresDate = parsedToken.getExpiresDate();
++    Date now = new Date();
++    assertTrue(expiresDate.after(now));
++    long diff = expiresDate.getTime() - now.getTime();
++    assertTrue(diff < 60000L && diff > 30000L);
++  }
++
++  @Test
++  public void testNegativeTTL() 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("-60000");
++    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);
++
++    JWT parsedToken = new JWTToken(cookie.getValue());
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++
++    Date expiresDate = parsedToken.getExpiresDate();
++    Date now = new Date();
++    assertTrue(expiresDate.after(now));
++    assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++  }
++
++  @Test
++  public void testOverflowTTL() 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(String.valueOf(Long.MAX_VALUE));
++    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);
++
++    JWT parsedToken = new JWTToken(cookie.getValue());
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++
++    Date expiresDate = parsedToken.getExpiresDate();
++    Date now = new Date();
++    assertTrue(expiresDate.after(now));
++    assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++  }
++
 +  /**
 +   * 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 JWT issueToken(Subject subject, String algorithm)
 +      throws TokenServiceException {
 +      Principal p = (Principal) subject.getPrincipals().toArray()[0];
 +      return issueToken(p, algorithm);
 +    }
 +
 +    @Override
 +    public JWT issueToken(Principal p, String algorithm)
 +      throws TokenServiceException {
 +      return issueToken(p, null, algorithm);
 +    }
 +
 +    @Override
 +    public JWT issueToken(Principal p, String audience, String algorithm)
 +      throws TokenServiceException {
 +      return issueToken(p, audience, algorithm, -1);
 +    }
 +
 +    @Override
 +    public boolean verifyToken(JWT token) throws TokenServiceException {
 +      JWSVerifier verifier = new RSASSAVerifier(publicKey);
 +      return token.verify(verifier);
 +    }
 +
 +    @Override
 +    public JWT 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 JWT 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");
-       }
++      JWT token = new JWTToken(algorithm, claimArray, audiences);
++      JWSSigner signer = new RSASSASigner(privateKey);
++      token.sign(signer);
 +
 +      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(JWT token, RSAPublicKey publicKey) throws TokenServiceException {
 +      JWSVerifier verifier = new RSASSAVerifier(publicKey);
 +      return token.verify(verifier);
 +    }
 +
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/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 1c16ab3,0000000..f8eb124
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,218 -1,0 +1,230 @@@
 +/**
 + * 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.security.cert.X509Certificate;
 +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 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";
 +  private static final String TOKEN_CLIENT_CERT_REQUIRED = "knox.token.client.cert.required";
 +  private static final String TOKEN_ALLOWED_PRINCIPALS = "knox.token.allowed.principals";
++  private static final String TOKEN_SIG_ALG = "knox.token.sigalg";
++  private static final long TOKEN_TTL_DEFAULT = 30000L;
 +  static final String RESOURCE_PATH = "knoxtoken/api/v1/token";
 +  private static TokenServiceMessages log = MessagesFactory.get( TokenServiceMessages.class );
-   private long tokenTTL = 30000l;
++  private long tokenTTL = TOKEN_TTL_DEFAULT;
 +  private List<String> targetAudiences = new ArrayList<>();
 +  private String tokenTargetUrl = null;
 +  private Map<String,Object> tokenClientDataMap = null;
 +  private ArrayList<String> allowedDNs = new ArrayList<>();
 +  private boolean clientCertRequired = false;
++  private String signatureAlgorithm = "RS256";
 +
 +  @Context
 +  HttpServletRequest request;
 +
 +  @Context
 +  HttpServletResponse response;
 +
 +  @Context
 +  ServletContext context;
 +
 +  @PostConstruct
 +  public void init() {
 +
 +    String audiences = context.getInitParameter(TOKEN_AUDIENCES_PARAM);
 +    if (audiences != null) {
 +      String[] auds = audiences.split(",");
 +      for (int i = 0; i < auds.length; i++) {
 +        targetAudiences.add(auds[i].trim());
 +      }
 +    }
 +
 +    String clientCert = context.getInitParameter(TOKEN_CLIENT_CERT_REQUIRED);
 +    clientCertRequired = "true".equals(clientCert);
 +
 +    String principals = context.getInitParameter(TOKEN_ALLOWED_PRINCIPALS);
 +    if (principals != null) {
 +      String[] dns = principals.split(";");
 +      for (int i = 0; i < dns.length; i++) {
 +        allowedDNs.add(dns[i]);
 +      }
 +    }
 +
 +    String ttl = context.getInitParameter(TOKEN_TTL_PARAM);
 +    if (ttl != null) {
 +      try {
 +        tokenTTL = Long.parseLong(ttl);
++        if (tokenTTL < -1 || (tokenTTL + System.currentTimeMillis() < 0)) {
++          log.invalidTokenTTLEncountered(ttl);
++          tokenTTL = TOKEN_TTL_DEFAULT;
++        }
 +      }
 +      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);
 +    }
++
++    String sigAlg = context.getInitParameter(TOKEN_SIG_ALG);
++    if (sigAlg != null) {
++      signatureAlgorithm = sigAlg;
++    }
 +  }
 +
 +  @GET
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  public Response doGet() {
 +    return getAuthenticationToken();
 +  }
 +
 +  @POST
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  public Response doPost() {
 +    return getAuthenticationToken();
 +  }
 +
 +  private X509Certificate extractCertificate(HttpServletRequest req) {
 +    X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
 +    if (null != certs && certs.length > 0) {
 +        return certs[0];
 +    }
 +    return null;
 +  }
 +
 +  private Response getAuthenticationToken() {
 +    if (clientCertRequired) {
 +      X509Certificate cert = extractCertificate(request);
 +      if (cert != null) {
 +        if (!allowedDNs.contains(cert.getSubjectDN().getName())) {
 +          return Response.status(403).entity("{ \"Unable to get token - untrusted client cert.\" }").build();
 +        }
 +      }
 +      else {
 +        return Response.status(403).entity("{ \"Unable to get token - client cert required.\" }").build();
 +      }
 +    }
 +    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.isEmpty()) {
-         token = ts.issueToken(p, "RS256", expires);
++        token = ts.issueToken(p, signatureAlgorithm, expires);
 +      } else {
-         token = ts.issueToken(p, targetAudiences, "RS256", expires);
++        token = ts.issueToken(p, targetAudiences, signatureAlgorithm, 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;
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
----------------------------------------------------------------------
diff --cc gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index b73b1b7,0000000..3753b27
mode 100644,000000..100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@@ -1,510 -1,0 +1,782 @@@
 +/**
 + * 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 org.apache.knox.gateway.service.knoxtoken.TokenResource;
 +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.security.PrimaryPrincipal;
 +
 +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;
 +
 +import java.util.Map;
 +
 +import javax.security.auth.Subject;
 +import javax.servlet.ServletContext;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +import javax.ws.rs.core.Response;
 +
 +import static org.junit.Assert.*;
 +
 +import java.io.PrintWriter;
 +import java.io.StringWriter;
 +import java.security.KeyPair;
 +import java.security.KeyPairGenerator;
 +import java.security.NoSuchAlgorithmException;
 +import java.security.Principal;
 +import java.security.cert.X509Certificate;
 +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.Date;
 +import java.util.HashMap;
 +import java.util.List;
 +
 +/**
 + * Some tests for the token service
 + */
 +public class TokenServiceResourceTest {
 +
 +  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 testTokenService() throws Exception {
 +    Assert.assertTrue(true);
 +  }
 +
 +  @Test
 +  public void testClientData() throws Exception {
 +    TokenResource tr = new TokenResource();
 +
 +    Map<String,Object> clientDataMap = new HashMap<>();
 +    tr.addClientDataToMap("cookie.name=hadoop-jwt,test=value".split(","), clientDataMap);
 +    Assert.assertTrue(clientDataMap.size() == 2);
 +
 +    clientDataMap = new HashMap<>();
 +    tr.addClientDataToMap("cookie.name=hadoop-jwt".split(","), clientDataMap);
 +    Assert.assertTrue(clientDataMap.size() == 1);
 +
 +    clientDataMap = new HashMap<>();
 +    tr.addClientDataToMap("".split(","), clientDataMap);
 +    Assert.assertTrue(clientDataMap.size() == 0);
 +  }
 +
 +  @Test
 +  public void testGetToken() throws Exception {
-     TokenResource tr = new TokenResource();
 +
 +    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
-     //tr.context = context;
-     // tr.init();
 +
 +    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +    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);
 +
 +    StringWriter writer = new StringWriter();
 +    PrintWriter printWriter = new PrintWriter(writer);
 +    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +    EasyMock.expect(response.getWriter()).andReturn(printWriter);
 +
 +    EasyMock.replay(principal, services, context, request, response);
 +
++    TokenResource tr = new TokenResource();
 +    tr.request = request;
 +    tr.response = response;
 +
 +    // Issue a token
 +    Response retResponse = tr.doGet();
 +
 +    assertEquals(200, retResponse.getStatus());
 +
 +    // Parse the response
 +    String retString = writer.toString();
 +    String accessToken = getTagValue(retString, "access_token");
 +    assertNotNull(accessToken);
 +    String expiry = getTagValue(retString, "expires_in");
 +    assertNotNull(expiry);
 +
 +    // Verify the token
-     JWTToken parsedToken = new JWTToken(accessToken);
++    JWT parsedToken = new JWTToken(accessToken);
 +    assertEquals("alice", parsedToken.getSubject());
 +    assertTrue(authority.verifyToken(parsedToken));
 +  }
 +
 +  @Test
 +  public void testAudiences() throws Exception {
 +
 +    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
 +    EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
 +    EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
 +    EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
 +    EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
 +
 +    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +    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);
 +
 +    StringWriter writer = new StringWriter();
 +    PrintWriter printWriter = new PrintWriter(writer);
 +    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +    EasyMock.expect(response.getWriter()).andReturn(printWriter);
 +
 +    EasyMock.replay(principal, services, context, request, response);
 +
 +    TokenResource tr = new TokenResource();
 +    tr.request = request;
 +    tr.response = response;
 +    tr.context = context;
 +    tr.init();
 +
 +    // Issue a token
 +    Response retResponse = tr.doGet();
 +
 +    assertEquals(200, retResponse.getStatus());
 +
 +    // Parse the response
 +    String retString = writer.toString();
 +    String accessToken = getTagValue(retString, "access_token");
 +    assertNotNull(accessToken);
 +    String expiry = getTagValue(retString, "expires_in");
 +    assertNotNull(expiry);
 +
 +    // Verify the token
-     JWTToken parsedToken = new JWTToken(accessToken);
++    JWT parsedToken = new JWTToken(accessToken);
 +    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"));
 +  }
 +
 +  @Test
 +  public void testAudiencesWhitespace() throws Exception {
 +
 +    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
 +    EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn(" recipient1, recipient2 ");
 +    EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
 +    EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
 +    EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
 +
 +    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +    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);
 +
 +    StringWriter writer = new StringWriter();
 +    PrintWriter printWriter = new PrintWriter(writer);
 +    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +    EasyMock.expect(response.getWriter()).andReturn(printWriter);
 +
 +    EasyMock.replay(principal, services, context, request, response);
 +
 +    TokenResource tr = new TokenResource();
 +    tr.request = request;
 +    tr.response = response;
 +    tr.context = context;
 +    tr.init();
 +
 +    // Issue a token
 +    Response retResponse = tr.doGet();
 +
 +    assertEquals(200, retResponse.getStatus());
 +
 +    // Parse the response
 +    String retString = writer.toString();
 +    String accessToken = getTagValue(retString, "access_token");
 +    assertNotNull(accessToken);
 +    String expiry = getTagValue(retString, "expires_in");
 +    assertNotNull(expiry);
 +
 +    // Verify the token
-     JWTToken parsedToken = new JWTToken(accessToken);
++    JWT parsedToken = new JWTToken(accessToken);
 +    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"));
 +  }
 +
 +  @Test
 +  public void testValidClientCert() throws Exception {
 +
 +    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
 +    EasyMock.expect(context.getInitParameter("knox.token.client.cert.required")).andReturn("true");
 +    EasyMock.expect(context.getInitParameter("knox.token.allowed.principals")).andReturn("CN=localhost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US");
 +
 +    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +    EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 +    X509Certificate trustedCertMock = EasyMock.createMock(X509Certificate.class);
 +    EasyMock.expect(trustedCertMock.getSubjectDN()).andReturn(new PrimaryPrincipal("CN=localhost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US")).anyTimes();
 +    ArrayList<X509Certificate> certArrayList = new ArrayList<X509Certificate>();
 +    certArrayList.add(trustedCertMock);
 +    X509Certificate[] certs = {};
 +    EasyMock.expect(request.getAttribute("javax.servlet.request.X509Certificate")).andReturn(certArrayList.toArray(certs)).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);
 +
 +    StringWriter writer = new StringWriter();
 +    PrintWriter printWriter = new PrintWriter(writer);
 +    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +    EasyMock.expect(response.getWriter()).andReturn(printWriter);
 +
 +    EasyMock.replay(principal, services, context, request, response, trustedCertMock);
 +
 +    TokenResource tr = new TokenResource();
 +    tr.request = request;
 +    tr.response = response;
 +    tr.context = context;
 +    tr.init();
 +
 +    // Issue a token
 +    Response retResponse = tr.doGet();
 +
 +    assertEquals(200, retResponse.getStatus());
 +
 +    // Parse the response
 +    String retString = writer.toString();
 +    String accessToken = getTagValue(retString, "access_token");
 +    assertNotNull(accessToken);
 +    String expiry = getTagValue(retString, "expires_in");
 +    assertNotNull(expiry);
 +
 +    // Verify the token
-     JWTToken parsedToken = new JWTToken(accessToken);
++    JWT parsedToken = new JWTToken(accessToken);
 +    assertEquals("alice", parsedToken.getSubject());
 +    assertTrue(authority.verifyToken(parsedToken));
 +  }
 +
 +  @Test
 +  public void testValidClientCertWrongUser() throws Exception {
 +
 +    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
 +    EasyMock.expect(context.getInitParameter("knox.token.client.cert.required")).andReturn("true");
 +    EasyMock.expect(context.getInitParameter("knox.token.allowed.principals")).andReturn("CN=remotehost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US");
 +
 +    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +    EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 +    X509Certificate trustedCertMock = EasyMock.createMock(X509Certificate.class);
 +    EasyMock.expect(trustedCertMock.getSubjectDN()).andReturn(new PrimaryPrincipal("CN=localhost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US")).anyTimes();
 +    ArrayList<X509Certificate> certArrayList = new ArrayList<X509Certificate>();
 +    certArrayList.add(trustedCertMock);
 +    X509Certificate[] certs = {};
 +    EasyMock.expect(request.getAttribute("javax.servlet.request.X509Certificate")).andReturn(certArrayList.toArray(certs)).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);
 +
 +    StringWriter writer = new StringWriter();
 +    PrintWriter printWriter = new PrintWriter(writer);
 +    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +    EasyMock.expect(response.getWriter()).andReturn(printWriter);
 +
 +    EasyMock.replay(principal, services, context, request, response, trustedCertMock);
 +
 +    TokenResource tr = new TokenResource();
 +    tr.request = request;
 +    tr.response = response;
 +    tr.context = context;
 +    tr.init();
 +
 +    // Issue a token
 +    Response retResponse = tr.doGet();
 +
 +    assertEquals(403, retResponse.getStatus());
 +  }
 +
 +  @Test
 +  public void testMissingClientCert() throws Exception {
 +
 +    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
 +    EasyMock.expect(context.getInitParameter("knox.token.client.cert.required")).andReturn("true");
 +    EasyMock.expect(context.getInitParameter("knox.token.allowed.principals")).andReturn("CN=remotehost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US");
 +
 +    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +    EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 +    EasyMock.expect(request.getAttribute("javax.servlet.request.X509Certificate")).andReturn(null).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);
 +
 +    StringWriter writer = new StringWriter();
 +    PrintWriter printWriter = new PrintWriter(writer);
 +    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +    EasyMock.expect(response.getWriter()).andReturn(printWriter);
 +
 +    EasyMock.replay(principal, services, context, request, response);
 +
 +    TokenResource tr = new TokenResource();
 +    tr.request = request;
 +    tr.response = response;
 +    tr.context = context;
 +    tr.init();
 +
 +    // Issue a token
 +    Response retResponse = tr.doGet();
 +
 +    assertEquals(403, retResponse.getStatus());
 +  }
 +
++  @Test
++  public void testSignatureAlgorithm() throws Exception {
++    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++    EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++    EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
++    EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++    EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++    EasyMock.expect(context.getInitParameter("knox.token.sigalg")).andReturn("RS512");
++
++    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++    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);
++
++    StringWriter writer = new StringWriter();
++    PrintWriter printWriter = new PrintWriter(writer);
++    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++    EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++    EasyMock.replay(principal, services, context, request, response);
++
++    TokenResource tr = new TokenResource();
++    tr.request = request;
++    tr.response = response;
++    tr.context = context;
++    tr.init();
++
++    // Issue a token
++    Response retResponse = tr.doGet();
++
++    assertEquals(200, retResponse.getStatus());
++
++    // Parse the response
++    String retString = writer.toString();
++    String accessToken = getTagValue(retString, "access_token");
++    assertNotNull(accessToken);
++    String expiry = getTagValue(retString, "expires_in");
++    assertNotNull(expiry);
++
++    // Verify the token
++    JWT parsedToken = new JWTToken(accessToken);
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++    assertTrue(parsedToken.getHeader().contains("RS512"));
++  }
++
++  @Test
++  public void testDefaultTTL() throws Exception {
++    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++    EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++    EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
++    EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++    EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++
++    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++    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);
++
++    StringWriter writer = new StringWriter();
++    PrintWriter printWriter = new PrintWriter(writer);
++    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++    EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++    EasyMock.replay(principal, services, context, request, response);
++
++    TokenResource tr = new TokenResource();
++    tr.request = request;
++    tr.response = response;
++    tr.context = context;
++    tr.init();
++
++    // Issue a token
++    Response retResponse = tr.doGet();
++
++    assertEquals(200, retResponse.getStatus());
++
++    // Parse the response
++    String retString = writer.toString();
++    String accessToken = getTagValue(retString, "access_token");
++    assertNotNull(accessToken);
++    String expiry = getTagValue(retString, "expires_in");
++    assertNotNull(expiry);
++
++    // Verify the token
++    JWT parsedToken = new JWTToken(accessToken);
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++
++    Date expiresDate = parsedToken.getExpiresDate();
++    Date now = new Date();
++    assertTrue(expiresDate.after(now));
++    assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++  }
++
++  @Test
++  public void testCustomTTL() throws Exception {
++    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++    EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++    EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn("60000");
++    EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++    EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++
++    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++    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);
++
++    StringWriter writer = new StringWriter();
++    PrintWriter printWriter = new PrintWriter(writer);
++    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++    EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++    EasyMock.replay(principal, services, context, request, response);
++
++    TokenResource tr = new TokenResource();
++    tr.request = request;
++    tr.response = response;
++    tr.context = context;
++    tr.init();
++
++    // Issue a token
++    Response retResponse = tr.doGet();
++
++    assertEquals(200, retResponse.getStatus());
++
++    // Parse the response
++    String retString = writer.toString();
++    String accessToken = getTagValue(retString, "access_token");
++    assertNotNull(accessToken);
++    String expiry = getTagValue(retString, "expires_in");
++    assertNotNull(expiry);
++
++    // Verify the token
++    JWT parsedToken = new JWTToken(accessToken);
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++
++    Date expiresDate = parsedToken.getExpiresDate();
++    Date now = new Date();
++    assertTrue(expiresDate.after(now));
++    long diff = expiresDate.getTime() - now.getTime();
++    assertTrue(diff < 60000L && diff > 30000L);
++  }
++
++  @Test
++  public void testNegativeTTL() throws Exception {
++    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++    EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++    EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn("-60000");
++    EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++    EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++
++    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++    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);
++
++    StringWriter writer = new StringWriter();
++    PrintWriter printWriter = new PrintWriter(writer);
++    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++    EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++    EasyMock.replay(principal, services, context, request, response);
++
++    TokenResource tr = new TokenResource();
++    tr.request = request;
++    tr.response = response;
++    tr.context = context;
++    tr.init();
++
++    // Issue a token
++    Response retResponse = tr.doGet();
++
++    assertEquals(200, retResponse.getStatus());
++
++    // Parse the response
++    String retString = writer.toString();
++    String accessToken = getTagValue(retString, "access_token");
++    assertNotNull(accessToken);
++    String expiry = getTagValue(retString, "expires_in");
++    assertNotNull(expiry);
++
++    // Verify the token
++    JWT parsedToken = new JWTToken(accessToken);
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++
++    Date expiresDate = parsedToken.getExpiresDate();
++    Date now = new Date();
++    assertTrue(expiresDate.after(now));
++    assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++  }
++
++  @Test
++  public void testOverflowTTL() throws Exception {
++    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++    EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++    EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(String.valueOf(Long.MAX_VALUE));
++    EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++    EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++
++    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++    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);
++
++    StringWriter writer = new StringWriter();
++    PrintWriter printWriter = new PrintWriter(writer);
++    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++    EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++    EasyMock.replay(principal, services, context, request, response);
++
++    TokenResource tr = new TokenResource();
++    tr.request = request;
++    tr.response = response;
++    tr.context = context;
++    tr.init();
++
++    // Issue a token
++    Response retResponse = tr.doGet();
++
++    assertEquals(200, retResponse.getStatus());
++
++    // Parse the response
++    String retString = writer.toString();
++    String accessToken = getTagValue(retString, "access_token");
++    assertNotNull(accessToken);
++    String expiry = getTagValue(retString, "expires_in");
++    assertNotNull(expiry);
++
++    // Verify the token
++    JWT parsedToken = new JWTToken(accessToken);
++    assertEquals("alice", parsedToken.getSubject());
++    assertTrue(authority.verifyToken(parsedToken));
++
++    Date expiresDate = parsedToken.getExpiresDate();
++    Date now = new Date();
++    assertTrue(expiresDate.after(now));
++    assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++  }
++
 +  private String getTagValue(String token, String tagName) {
 +    String searchString = tagName + "\":";
 +    String value = token.substring(token.indexOf(searchString) + searchString.length());
 +    if (value.startsWith("\"")) {
 +      value = value.substring(1);
 +    }
 +    if (value.contains("\"")) {
 +      return value.substring(0, value.indexOf("\""));
 +    } else if (value.contains(",")) {
 +      return value.substring(0, value.indexOf(","));
 +    } else {
 +      return value.substring(0, value.length() - 1);
 +    }
 +  }
 +
 +  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 JWT issueToken(Subject subject, String algorithm)
 +      throws TokenServiceException {
 +      Principal p = (Principal) subject.getPrincipals().toArray()[0];
 +      return issueToken(p, algorithm);
 +    }
 +
 +    @Override
 +    public JWT issueToken(Principal p, String algorithm)
 +      throws TokenServiceException {
 +      return issueToken(p, null, algorithm);
 +    }
 +
 +    @Override
 +    public JWT issueToken(Principal p, String audience, String algorithm)
 +      throws TokenServiceException {
 +      return issueToken(p, audience, algorithm, -1);
 +    }
 +
 +    @Override
 +    public boolean verifyToken(JWT token) throws TokenServiceException {
 +      JWSVerifier verifier = new RSASSAVerifier(publicKey);
 +      return token.verify(verifier);
 +    }
 +
 +    @Override
 +    public JWT issueToken(Principal p, String audience, String algorithm,
 +                               long expires) throws TokenServiceException {
 +      ArrayList<String> audiences = null;
 +      if (audience != null) {
 +        audiences = new ArrayList<String>();
 +        audiences.add(audience);
 +      }
 +      return issueToken(p, audiences, algorithm, expires);
 +    }
 +
 +    @Override
 +    public JWT 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");
-       }
++      JWT token = new JWTToken(algorithm, claimArray, audiences);
++      JWSSigner signer = new RSASSASigner(privateKey);
++      token.sign(signer);
 +
 +      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(JWT token, RSAPublicKey publicKey) throws TokenServiceException {
 +      JWSVerifier verifier = new RSASSAVerifier(publicKey);
 +      return token.verify(verifier);
 +    }
 +
 +  }
 +
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-shell/src/main/java/org/apache/knox/gateway/shell/job/Sqoop.java
----------------------------------------------------------------------
diff --cc gateway-shell/src/main/java/org/apache/knox/gateway/shell/job/Sqoop.java
index ec9f907,0000000..072b58d
mode 100644,000000..100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/job/Sqoop.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/job/Sqoop.java
@@@ -1,99 -1,0 +1,99 @@@
 +/**
 + * 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.shell.job;
 +
 +import com.jayway.jsonpath.JsonPath;
 +import org.apache.knox.gateway.shell.AbstractRequest;
 +import org.apache.knox.gateway.shell.BasicResponse;
 +import org.apache.knox.gateway.shell.Hadoop;
 +import org.apache.http.HttpResponse;
 +import org.apache.http.NameValuePair;
 +import org.apache.http.client.entity.UrlEncodedFormEntity;
 +import org.apache.http.client.methods.HttpPost;
 +import org.apache.http.client.utils.URIBuilder;
 +
 +import java.io.IOException;
 +import java.util.ArrayList;
 +import java.util.List;
 +import java.util.concurrent.Callable;
 +
 +public class Sqoop {
 +
-   static class Request extends AbstractRequest<Response> {
++  public static class Request extends AbstractRequest<Response> {
 +
 +    private String statusDir;
 +    List<NameValuePair> params = new ArrayList<NameValuePair>();
 +
 +    public Request( Hadoop session ) {
 +      super( session );
 +    }
 +
 +    public Request command( String command ) {
 +      addParam( params, "command", command );
 +      return this;
 +    }
 +
 +    public Request libdir( String libdir ) {
 +      addParam( params, "libdir", libdir );
 +      return this;
 +    }
 +
 +    public Request files( String files ) {
 +      addParam( params, "files", files );
 +      return this;
 +    }
 +
 +    public Request optionsfile( String optionsFile ) {
 +      addParam( params, "optionsfile", optionsFile );
 +      return this;
 +    }
 +
 +    public Request statusDir( String dir ) {
 +      this.statusDir = dir;
 +      return this;
 +    }
 +
 +    protected Callable<Response> callable() {
 +      return new Callable<Response>() {
 +        @Override
 +        public Response call() throws Exception {
 +          URIBuilder uri = uri( Job.SERVICE_PATH, "/sqoop" );
 +          addParam( params, "statusdir", statusDir );
 +          UrlEncodedFormEntity form = new UrlEncodedFormEntity( params );
 +          HttpPost request = new HttpPost( uri.build() );
 +          request.setEntity( form );
 +          return new Response( execute( request ) );
 +        }
 +      };
 +    }
 +
 +  }
 +
 +  public static class Response extends BasicResponse {
 +
 +    protected Response( HttpResponse response ) {
 +      super( response );
 +    }
 +
 +    public String getJobId() throws IOException {
 +      return JsonPath.read( getString(), "$.id" );
 +    }
 +
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWT.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWT.java
index 8638da5,0000000..bd7cd5c
mode 100644,000000..100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWT.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWT.java
@@@ -1,63 -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.services.security.token.impl;
 +
 +import java.util.Date;
 +
 +import com.nimbusds.jose.JWSSigner;
 +import com.nimbusds.jose.JWSVerifier;
 +
 +public interface JWT {
 +
 +  String PRINCIPAL = "prn";
 +  String SUBJECT = "sub";
 +  String ISSUER = "iss";
 +  String AUDIENCE = "aud";
 +  String EXPIRES = "exp";
++  String NOT_BEFORE = "nbf";
 +
 +  String getPayload();
 +
 +  void setSignaturePayload(byte[] payload);
 +
 +  byte[] getSignaturePayload();
 +
 +  String getClaim(String claimName);
 +
 +  String getPrincipal();
 +
 +  String getIssuer();
 +
 +  String getAudience();
 +
 +  public String[] getAudienceClaims();
 +
 +  String getExpires();
 +
 +  Date getExpiresDate();
 +
++  Date getNotBeforeDate();
++
 +  String getSubject();
 +
 +  String getHeader();
 +
 +  String getClaims();
 +
 +  void sign(JWSSigner signer);
 +
 +  boolean verify(JWSVerifier verifier);
 +
 +}