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); + +}