KNOX-1022 - Configuring knox token ttl to higher value generates an access token which is not valid
Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/aa62fa2d Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/aa62fa2d Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/aa62fa2d Branch: refs/heads/KNOX-998-Package_Restructuring Commit: aa62fa2dbdca59b175eefb62e97b5528f40d076b Parents: 986615f Author: Colm O hEigeartaigh <cohei...@apache.org> Authored: Fri Oct 20 11:14:23 2017 +0100 Committer: Colm O hEigeartaigh <cohei...@apache.org> Committed: Fri Oct 20 11:14:23 2017 +0100 ---------------------------------------------------------------------- .../gateway/service/knoxsso/WebSSOResource.java | 7 +- .../service/knoxsso/WebSSOResourceTest.java | 230 +++++++++++++++++++ .../service/knoxtoken/TokenResource.java | 7 +- .../knoxtoken/TokenServiceResourceTest.java | 226 ++++++++++++++++++ 4 files changed, 468 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/aa62fa2d/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java ---------------------------------------------------------------------- diff --git a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java index 36aa075..97b0441 100644 --- a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java +++ b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java @@ -68,12 +68,13 @@ public class WebSSOResource { 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}/.*$"; + private static final long TOKEN_TTL_DEFAULT = 30000L; 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 long tokenTTL = TOKEN_TTL_DEFAULT; private String whitelist = null; private String domainSuffix = null; private List<String> targetAudiences = new ArrayList<>(); @@ -137,6 +138,10 @@ public class WebSSOResource { 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); http://git-wip-us.apache.org/repos/asf/knox/blob/aa62fa2d/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java ---------------------------------------------------------------------- diff --git a/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java b/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java index 516f9ae..dedc912 100644 --- a/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java +++ b/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java @@ -30,6 +30,7 @@ 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; @@ -341,6 +342,235 @@ public class WebSSOResourceTest { 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 */ http://git-wip-us.apache.org/repos/asf/knox/blob/aa62fa2d/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java ---------------------------------------------------------------------- diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java index afa6a3a..6135490 100644 --- a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java +++ b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java @@ -58,9 +58,10 @@ public class TokenResource { 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; @@ -103,6 +104,10 @@ public class TokenResource { 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); http://git-wip-us.apache.org/repos/asf/knox/blob/aa62fa2d/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java ---------------------------------------------------------------------- diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java index 80f359d..3e25440 100644 --- a/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java +++ b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java @@ -56,6 +56,7 @@ 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; @@ -456,6 +457,231 @@ public class TokenServiceResourceTest { 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());