You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by mo...@apache.org on 2018/01/11 17:38:38 UTC
[11/53] [abbrv] knox git commit: Merge branch 'master' into
KNOX-998-Package_Restructuring
http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
----------------------------------------------------------------------
diff --cc gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
index 6b8411e,0000000..0eb717e
mode 100644,000000..100644
--- a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
+++ b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
@@@ -1,410 -1,0 +1,689 @@@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.service.knoxsso;
+
+import org.apache.knox.gateway.util.RegExUtils;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
++import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.knox.gateway.services.GatewayServices;
+import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
+import org.apache.knox.gateway.services.security.token.TokenServiceException;
+import org.apache.knox.gateway.services.security.token.impl.JWT;
+import org.apache.knox.gateway.services.security.token.impl.JWTToken;
+import org.apache.knox.gateway.util.RegExUtils;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+
+/**
+ * Some tests for the Knox SSO service.
+ */
+public class WebSSOResourceTest {
+
+ protected static RSAPublicKey publicKey;
+ protected static RSAPrivateKey privateKey;
+
+ @BeforeClass
+ public static void setup() throws Exception, NoSuchAlgorithmException {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+ kpg.initialize(1024);
+ KeyPair KPair = kpg.generateKeyPair();
+
+ publicKey = (RSAPublicKey) KPair.getPublic();
+ privateKey = (RSAPrivateKey) KPair.getPrivate();
+ }
+
+ @Test
+ public void testWhitelistMatching() throws Exception {
+ String whitelist = "^https?://.*example.com:8080/.*$;" +
+ "^https?://.*example.com/.*$;" +
+ "^https?://.*example2.com:\\d{0,9}/.*$;" +
+ "^https://.*example3.com:\\d{0,9}/.*$;" +
+ "^https?://localhost:\\d{0,9}/.*$;^/.*$";
+
+ // match on explicit hostname/domain and port
+ Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
+ "http://host.example.com:8080/"));
+ // match on non-required port
+ Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
+ "http://host.example.com/"));
+ // match on required but any port
+ Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
+ "http://host.example2.com:1234/"));
+ // fail on missing port
+ Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
+ "http://host.example2.com/"));
+ // fail on invalid port
+ Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
+ "http://host.example.com:8081/"));
+ // fail on alphanumeric port
+ Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
+ "http://host.example.com:A080/"));
+ // fail on invalid hostname/domain
+ Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
+ "http://host.example.net:8080/"));
+ // fail on required port
+ Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
+ "http://host.example2.com/"));
+ // fail on required https
+ Assert.assertFalse("Matched whitelist inappropriately", RegExUtils.checkWhitelist(whitelist,
+ "http://host.example3.com/"));
+ // match on localhost and port
+ Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
+ "http://localhost:8080/"));
+ // match on local/relative path
+ Assert.assertTrue("Failed to match whitelist", RegExUtils.checkWhitelist(whitelist,
+ "/local/resource/"));
+ }
+
+ @Test
+ public void testGetToken() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
+ EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
+ CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
+
+ EasyMock.replay(principal, services, context, request);
+
+ WebSSOResource webSSOResponse = new WebSSOResource();
+ webSSOResponse.request = request;
+ webSSOResponse.response = responseWrapper;
+ webSSOResponse.context = context;
+ webSSOResponse.init();
+
+ // Issue a token
+ webSSOResponse.doGet();
+
+ // Check the cookie
+ Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
+ assertNotNull(cookie);
+
- JWTToken parsedToken = new JWTToken(cookie.getValue());
++ JWT parsedToken = new JWTToken(cookie.getValue());
+ assertEquals("alice", parsedToken.getSubject());
+ assertTrue(authority.verifyToken(parsedToken));
+ }
+
+ @Test
+ public void testAudiences() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn("recipient1,recipient2");
+ EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
+ EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
+ CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
+
+ EasyMock.replay(principal, services, context, request);
+
+ WebSSOResource webSSOResponse = new WebSSOResource();
+ webSSOResponse.request = request;
+ webSSOResponse.response = responseWrapper;
+ webSSOResponse.context = context;
+ webSSOResponse.init();
+
+ // Issue a token
+ webSSOResponse.doGet();
+
+ // Check the cookie
+ Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
+ assertNotNull(cookie);
+
- JWTToken parsedToken = new JWTToken(cookie.getValue());
++ JWT parsedToken = new JWTToken(cookie.getValue());
+ assertEquals("alice", parsedToken.getSubject());
+ assertTrue(authority.verifyToken(parsedToken));
+
+ // Verify the audiences
+ List<String> audiences = Arrays.asList(parsedToken.getAudienceClaims());
+ assertEquals(2, audiences.size());
+ assertTrue(audiences.contains("recipient1"));
+ assertTrue(audiences.contains("recipient2"));
+ }
+
+ @Test
+ public void testAudiencesWhitespace() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(" recipient1, recipient2 ");
+ EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
+ EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
+ CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
+
+ EasyMock.replay(principal, services, context, request);
+
+ WebSSOResource webSSOResponse = new WebSSOResource();
+ webSSOResponse.request = request;
+ webSSOResponse.response = responseWrapper;
+ webSSOResponse.context = context;
+ webSSOResponse.init();
+
+ // Issue a token
+ webSSOResponse.doGet();
+
+ // Check the cookie
+ Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
+ assertNotNull(cookie);
+
+ JWTToken parsedToken = new JWTToken(cookie.getValue());
+ assertEquals("alice", parsedToken.getSubject());
+ assertTrue(authority.verifyToken(parsedToken));
+
+ // Verify the audiences
+ List<String> audiences = Arrays.asList(parsedToken.getAudienceClaims());
+ assertEquals(2, audiences.size());
+ assertTrue(audiences.contains("recipient1"));
+ assertTrue(audiences.contains("recipient2"));
+ }
+
++ @Test
++ public void testSignatureAlgorithm() throws Exception {
++
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.sigalg")).andReturn("RS512");
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
++ EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
++ CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
++
++ EasyMock.replay(principal, services, context, request);
++
++ WebSSOResource webSSOResponse = new WebSSOResource();
++ webSSOResponse.request = request;
++ webSSOResponse.response = responseWrapper;
++ webSSOResponse.context = context;
++ webSSOResponse.init();
++
++ // Issue a token
++ webSSOResponse.doGet();
++
++ // Check the cookie
++ Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
++ assertNotNull(cookie);
++
++ JWT parsedToken = new JWTToken(cookie.getValue());
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++ assertTrue(parsedToken.getHeader().contains("RS512"));
++ }
++
++ @Test
++ public void testDefaultTTL() throws Exception {
++
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
++ EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
++ CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
++
++ EasyMock.replay(principal, services, context, request);
++
++ WebSSOResource webSSOResponse = new WebSSOResource();
++ webSSOResponse.request = request;
++ webSSOResponse.response = responseWrapper;
++ webSSOResponse.context = context;
++ webSSOResponse.init();
++
++ // Issue a token
++ webSSOResponse.doGet();
++
++ // Check the cookie
++ Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
++ assertNotNull(cookie);
++
++ JWT parsedToken = new JWTToken(cookie.getValue());
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++
++ Date expiresDate = parsedToken.getExpiresDate();
++ Date now = new Date();
++ assertTrue(expiresDate.after(now));
++ assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++ }
++
++ @Test
++ public void testCustomTTL() throws Exception {
++
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn("60000");
++ EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
++ EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
++ CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
++
++ EasyMock.replay(principal, services, context, request);
++
++ WebSSOResource webSSOResponse = new WebSSOResource();
++ webSSOResponse.request = request;
++ webSSOResponse.response = responseWrapper;
++ webSSOResponse.context = context;
++ webSSOResponse.init();
++
++ // Issue a token
++ webSSOResponse.doGet();
++
++ // Check the cookie
++ Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
++ assertNotNull(cookie);
++
++ JWT parsedToken = new JWTToken(cookie.getValue());
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++
++ Date expiresDate = parsedToken.getExpiresDate();
++ Date now = new Date();
++ assertTrue(expiresDate.after(now));
++ long diff = expiresDate.getTime() - now.getTime();
++ assertTrue(diff < 60000L && diff > 30000L);
++ }
++
++ @Test
++ public void testNegativeTTL() throws Exception {
++
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn("-60000");
++ EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
++ EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
++ CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
++
++ EasyMock.replay(principal, services, context, request);
++
++ WebSSOResource webSSOResponse = new WebSSOResource();
++ webSSOResponse.request = request;
++ webSSOResponse.response = responseWrapper;
++ webSSOResponse.context = context;
++ webSSOResponse.init();
++
++ // Issue a token
++ webSSOResponse.doGet();
++
++ // Check the cookie
++ Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
++ assertNotNull(cookie);
++
++ JWT parsedToken = new JWTToken(cookie.getValue());
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++
++ Date expiresDate = parsedToken.getExpiresDate();
++ Date now = new Date();
++ assertTrue(expiresDate.after(now));
++ assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++ }
++
++ @Test
++ public void testOverflowTTL() throws Exception {
++
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(String.valueOf(Long.MAX_VALUE));
++ EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
++ EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
++ CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
++
++ EasyMock.replay(principal, services, context, request);
++
++ WebSSOResource webSSOResponse = new WebSSOResource();
++ webSSOResponse.request = request;
++ webSSOResponse.response = responseWrapper;
++ webSSOResponse.context = context;
++ webSSOResponse.init();
++
++ // Issue a token
++ webSSOResponse.doGet();
++
++ // Check the cookie
++ Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
++ assertNotNull(cookie);
++
++ JWT parsedToken = new JWTToken(cookie.getValue());
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++
++ Date expiresDate = parsedToken.getExpiresDate();
++ Date now = new Date();
++ assertTrue(expiresDate.after(now));
++ assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++ }
++
+ /**
+ * A wrapper for HttpServletResponseWrapper to store the cookies
+ */
+ private static class CookieResponseWrapper extends HttpServletResponseWrapper {
+
+ private ServletOutputStream outputStream;
+ private Map<String, Cookie> cookies = new HashMap<>();
+
+ public CookieResponseWrapper(HttpServletResponse response) {
+ super(response);
+ }
+
+ public CookieResponseWrapper(HttpServletResponse response, ServletOutputStream outputStream) {
+ super(response);
+ this.outputStream = outputStream;
+ }
+
+ @Override
+ public ServletOutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ @Override
+ public void addCookie(Cookie cookie) {
+ super.addCookie(cookie);
+ cookies.put(cookie.getName(), cookie);
+ }
+
+ public Cookie getCookie(String name) {
+ return cookies.get(name);
+ }
+
+ }
+
+ private static class TestJWTokenAuthority implements JWTokenAuthority {
+
+ private RSAPublicKey publicKey;
+ private RSAPrivateKey privateKey;
+
+ public TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
+ this.publicKey = publicKey;
+ this.privateKey = privateKey;
+ }
+
+ @Override
+ public JWT issueToken(Subject subject, String algorithm)
+ throws TokenServiceException {
+ Principal p = (Principal) subject.getPrincipals().toArray()[0];
+ return issueToken(p, algorithm);
+ }
+
+ @Override
+ public JWT issueToken(Principal p, String algorithm)
+ throws TokenServiceException {
+ return issueToken(p, null, algorithm);
+ }
+
+ @Override
+ public JWT issueToken(Principal p, String audience, String algorithm)
+ throws TokenServiceException {
+ return issueToken(p, audience, algorithm, -1);
+ }
+
+ @Override
+ public boolean verifyToken(JWT token) throws TokenServiceException {
+ JWSVerifier verifier = new RSASSAVerifier(publicKey);
+ return token.verify(verifier);
+ }
+
+ @Override
+ public JWT issueToken(Principal p, String audience, String algorithm,
+ long expires) throws TokenServiceException {
+ List<String> audiences = null;
+ if (audience != null) {
+ audiences = new ArrayList<String>();
+ audiences.add(audience);
+ }
+ return issueToken(p, audiences, algorithm, expires);
+ }
+
+ @Override
+ public JWT issueToken(Principal p, List<String> audiences, String algorithm,
+ long expires) throws TokenServiceException {
+ String[] claimArray = new String[4];
+ claimArray[0] = "KNOXSSO";
+ claimArray[1] = p.getName();
+ claimArray[2] = null;
+ if (expires == -1) {
+ claimArray[3] = null;
+ } else {
+ claimArray[3] = String.valueOf(expires);
+ }
+
- JWTToken token = null;
- if ("RS256".equals(algorithm)) {
- token = new JWTToken("RS256", claimArray, audiences);
- JWSSigner signer = new RSASSASigner(privateKey);
- token.sign(signer);
- } else {
- throw new TokenServiceException("Cannot issue token - Unsupported algorithm");
- }
++ JWT token = new JWTToken(algorithm, claimArray, audiences);
++ JWSSigner signer = new RSASSASigner(privateKey);
++ token.sign(signer);
+
+ return token;
+ }
+
+ @Override
+ public JWT issueToken(Principal p, String algorithm, long expiry)
+ throws TokenServiceException {
+ return issueToken(p, Collections.<String>emptyList(), algorithm, expiry);
+ }
+
+ @Override
+ public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException {
+ JWSVerifier verifier = new RSASSAVerifier(publicKey);
+ return token.verify(verifier);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
----------------------------------------------------------------------
diff --cc gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
index 1c16ab3,0000000..f8eb124
mode 100644,000000..100644
--- a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
@@@ -1,218 -1,0 +1,230 @@@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.service.knoxtoken;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.services.GatewayServices;
+import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
+import org.apache.knox.gateway.services.security.token.TokenServiceException;
+import org.apache.knox.gateway.services.security.token.impl.JWT;
+import org.apache.knox.gateway.util.JsonUtils;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+
+@Path( TokenResource.RESOURCE_PATH )
+public class TokenResource {
+ private static final String EXPIRES_IN = "expires_in";
+ private static final String TOKEN_TYPE = "token_type";
+ private static final String ACCESS_TOKEN = "access_token";
+ private static final String TARGET_URL = "target_url";
- private static final String BEARER = "Bearer ";
++ private static final String BEARER = "Bearer";
+ private static final String TOKEN_TTL_PARAM = "knox.token.ttl";
+ private static final String TOKEN_AUDIENCES_PARAM = "knox.token.audiences";
+ private static final String TOKEN_TARGET_URL = "knox.token.target.url";
+ private static final String TOKEN_CLIENT_DATA = "knox.token.client.data";
+ private static final String TOKEN_CLIENT_CERT_REQUIRED = "knox.token.client.cert.required";
+ private static final String TOKEN_ALLOWED_PRINCIPALS = "knox.token.allowed.principals";
++ private static final String TOKEN_SIG_ALG = "knox.token.sigalg";
++ private static final long TOKEN_TTL_DEFAULT = 30000L;
+ static final String RESOURCE_PATH = "knoxtoken/api/v1/token";
+ private static TokenServiceMessages log = MessagesFactory.get( TokenServiceMessages.class );
- private long tokenTTL = 30000l;
++ private long tokenTTL = TOKEN_TTL_DEFAULT;
+ private List<String> targetAudiences = new ArrayList<>();
+ private String tokenTargetUrl = null;
+ private Map<String,Object> tokenClientDataMap = null;
+ private ArrayList<String> allowedDNs = new ArrayList<>();
+ private boolean clientCertRequired = false;
++ private String signatureAlgorithm = "RS256";
+
+ @Context
+ HttpServletRequest request;
+
+ @Context
+ HttpServletResponse response;
+
+ @Context
+ ServletContext context;
+
+ @PostConstruct
+ public void init() {
+
+ String audiences = context.getInitParameter(TOKEN_AUDIENCES_PARAM);
+ if (audiences != null) {
+ String[] auds = audiences.split(",");
+ for (int i = 0; i < auds.length; i++) {
+ targetAudiences.add(auds[i].trim());
+ }
+ }
+
+ String clientCert = context.getInitParameter(TOKEN_CLIENT_CERT_REQUIRED);
+ clientCertRequired = "true".equals(clientCert);
+
+ String principals = context.getInitParameter(TOKEN_ALLOWED_PRINCIPALS);
+ if (principals != null) {
+ String[] dns = principals.split(";");
+ for (int i = 0; i < dns.length; i++) {
+ allowedDNs.add(dns[i]);
+ }
+ }
+
+ String ttl = context.getInitParameter(TOKEN_TTL_PARAM);
+ if (ttl != null) {
+ try {
+ tokenTTL = Long.parseLong(ttl);
++ if (tokenTTL < -1 || (tokenTTL + System.currentTimeMillis() < 0)) {
++ log.invalidTokenTTLEncountered(ttl);
++ tokenTTL = TOKEN_TTL_DEFAULT;
++ }
+ }
+ catch (NumberFormatException nfe) {
+ log.invalidTokenTTLEncountered(ttl);
+ }
+ }
+
+ tokenTargetUrl = context.getInitParameter(TOKEN_TARGET_URL);
+
+ String clientData = context.getInitParameter(TOKEN_CLIENT_DATA);
+ if (clientData != null) {
+ tokenClientDataMap = new HashMap<>();
+ String[] tokenClientData = clientData.split(",");
+ addClientDataToMap(tokenClientData, tokenClientDataMap);
+ }
++
++ String sigAlg = context.getInitParameter(TOKEN_SIG_ALG);
++ if (sigAlg != null) {
++ signatureAlgorithm = sigAlg;
++ }
+ }
+
+ @GET
+ @Produces({APPLICATION_JSON, APPLICATION_XML})
+ public Response doGet() {
+ return getAuthenticationToken();
+ }
+
+ @POST
+ @Produces({APPLICATION_JSON, APPLICATION_XML})
+ public Response doPost() {
+ return getAuthenticationToken();
+ }
+
+ private X509Certificate extractCertificate(HttpServletRequest req) {
+ X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
+ if (null != certs && certs.length > 0) {
+ return certs[0];
+ }
+ return null;
+ }
+
+ private Response getAuthenticationToken() {
+ if (clientCertRequired) {
+ X509Certificate cert = extractCertificate(request);
+ if (cert != null) {
+ if (!allowedDNs.contains(cert.getSubjectDN().getName())) {
+ return Response.status(403).entity("{ \"Unable to get token - untrusted client cert.\" }").build();
+ }
+ }
+ else {
+ return Response.status(403).entity("{ \"Unable to get token - client cert required.\" }").build();
+ }
+ }
+ GatewayServices services = (GatewayServices) request.getServletContext()
+ .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
+
+ JWTokenAuthority ts = services.getService(GatewayServices.TOKEN_SERVICE);
+ Principal p = ((HttpServletRequest)request).getUserPrincipal();
+ long expires = getExpiry();
+
+ try {
+ JWT token = null;
+ if (targetAudiences.isEmpty()) {
- token = ts.issueToken(p, "RS256", expires);
++ token = ts.issueToken(p, signatureAlgorithm, expires);
+ } else {
- token = ts.issueToken(p, targetAudiences, "RS256", expires);
++ token = ts.issueToken(p, targetAudiences, signatureAlgorithm, expires);
+ }
+
+ if (token != null) {
+ String accessToken = token.toString();
+
+ HashMap<String, Object> map = new HashMap<>();
+ map.put(ACCESS_TOKEN, accessToken);
+ map.put(TOKEN_TYPE, BEARER);
+ map.put(EXPIRES_IN, expires);
+ if (tokenTargetUrl != null) {
+ map.put(TARGET_URL, tokenTargetUrl);
+ }
+ if (tokenClientDataMap != null) {
+ map.putAll(tokenClientDataMap);
+ }
+
+ String jsonResponse = JsonUtils.renderAsJsonString(map);
+
+ response.getWriter().write(jsonResponse);
+ return Response.ok().build();
+ }
+ else {
+ return Response.serverError().build();
+ }
+ }
+ catch (TokenServiceException | IOException e) {
+ log.unableToIssueToken(e);
+ }
+ return Response.ok().entity("{ \"Unable to acquire token.\" }").build();
+ }
+
+ void addClientDataToMap(String[] tokenClientData,
+ Map<String,Object> map) {
+ String[] kv = null;
+ for (int i = 0; i < tokenClientData.length; i++) {
+ kv = tokenClientData[i].split("=");
+ if (kv.length == 2) {
+ map.put(kv[0], kv[1]);
+ }
+ }
+ }
+
+ private long getExpiry() {
+ long expiry = 0l;
+ if (tokenTTL == -1) {
+ expiry = -1;
+ }
+ else {
+ expiry = System.currentTimeMillis() + tokenTTL;
+ }
+ return expiry;
+ }
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
----------------------------------------------------------------------
diff --cc gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index b73b1b7,0000000..3753b27
mode 100644,000000..100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@@ -1,510 -1,0 +1,782 @@@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.service.knoxtoken;
+
+import org.apache.knox.gateway.service.knoxtoken.TokenResource;
+import org.apache.knox.gateway.services.GatewayServices;
+import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
+import org.apache.knox.gateway.services.security.token.TokenServiceException;
+import org.apache.knox.gateway.services.security.token.impl.JWT;
+import org.apache.knox.gateway.services.security.token.impl.JWTToken;
+import org.apache.knox.gateway.security.PrimaryPrincipal;
+
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.*;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
++import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Some tests for the token service
+ */
+public class TokenServiceResourceTest {
+
+ protected static RSAPublicKey publicKey;
+ protected static RSAPrivateKey privateKey;
+
+ @BeforeClass
+ public static void setup() throws Exception, NoSuchAlgorithmException {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+ kpg.initialize(1024);
+ KeyPair KPair = kpg.generateKeyPair();
+
+ publicKey = (RSAPublicKey) KPair.getPublic();
+ privateKey = (RSAPrivateKey) KPair.getPrivate();
+ }
+
+ @Test
+ public void testTokenService() throws Exception {
+ Assert.assertTrue(true);
+ }
+
+ @Test
+ public void testClientData() throws Exception {
+ TokenResource tr = new TokenResource();
+
+ Map<String,Object> clientDataMap = new HashMap<>();
+ tr.addClientDataToMap("cookie.name=hadoop-jwt,test=value".split(","), clientDataMap);
+ Assert.assertTrue(clientDataMap.size() == 2);
+
+ clientDataMap = new HashMap<>();
+ tr.addClientDataToMap("cookie.name=hadoop-jwt".split(","), clientDataMap);
+ Assert.assertTrue(clientDataMap.size() == 1);
+
+ clientDataMap = new HashMap<>();
+ tr.addClientDataToMap("".split(","), clientDataMap);
+ Assert.assertTrue(clientDataMap.size() == 0);
+ }
+
+ @Test
+ public void testGetToken() throws Exception {
- TokenResource tr = new TokenResource();
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
- //tr.context = context;
- // tr.init();
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response);
+
++ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(200, retResponse.getStatus());
+
+ // Parse the response
+ String retString = writer.toString();
+ String accessToken = getTagValue(retString, "access_token");
+ assertNotNull(accessToken);
+ String expiry = getTagValue(retString, "expires_in");
+ assertNotNull(expiry);
+
+ // Verify the token
- JWTToken parsedToken = new JWTToken(accessToken);
++ JWT parsedToken = new JWTToken(accessToken);
+ assertEquals("alice", parsedToken.getSubject());
+ assertTrue(authority.verifyToken(parsedToken));
+ }
+
+ @Test
+ public void testAudiences() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
+ EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response);
+
+ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+ tr.context = context;
+ tr.init();
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(200, retResponse.getStatus());
+
+ // Parse the response
+ String retString = writer.toString();
+ String accessToken = getTagValue(retString, "access_token");
+ assertNotNull(accessToken);
+ String expiry = getTagValue(retString, "expires_in");
+ assertNotNull(expiry);
+
+ // Verify the token
- JWTToken parsedToken = new JWTToken(accessToken);
++ JWT parsedToken = new JWTToken(accessToken);
+ assertEquals("alice", parsedToken.getSubject());
+ assertTrue(authority.verifyToken(parsedToken));
+
+ // Verify the audiences
+ List<String> audiences = Arrays.asList(parsedToken.getAudienceClaims());
+ assertEquals(2, audiences.size());
+ assertTrue(audiences.contains("recipient1"));
+ assertTrue(audiences.contains("recipient2"));
+ }
+
+ @Test
+ public void testAudiencesWhitespace() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn(" recipient1, recipient2 ");
+ EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response);
+
+ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+ tr.context = context;
+ tr.init();
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(200, retResponse.getStatus());
+
+ // Parse the response
+ String retString = writer.toString();
+ String accessToken = getTagValue(retString, "access_token");
+ assertNotNull(accessToken);
+ String expiry = getTagValue(retString, "expires_in");
+ assertNotNull(expiry);
+
+ // Verify the token
- JWTToken parsedToken = new JWTToken(accessToken);
++ JWT parsedToken = new JWTToken(accessToken);
+ assertEquals("alice", parsedToken.getSubject());
+ assertTrue(authority.verifyToken(parsedToken));
+
+ // Verify the audiences
+ List<String> audiences = Arrays.asList(parsedToken.getAudienceClaims());
+ assertEquals(2, audiences.size());
+ assertTrue(audiences.contains("recipient1"));
+ assertTrue(audiences.contains("recipient2"));
+ }
+
+ @Test
+ public void testValidClientCert() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knox.token.client.cert.required")).andReturn("true");
+ EasyMock.expect(context.getInitParameter("knox.token.allowed.principals")).andReturn("CN=localhost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US");
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+ X509Certificate trustedCertMock = EasyMock.createMock(X509Certificate.class);
+ EasyMock.expect(trustedCertMock.getSubjectDN()).andReturn(new PrimaryPrincipal("CN=localhost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US")).anyTimes();
+ ArrayList<X509Certificate> certArrayList = new ArrayList<X509Certificate>();
+ certArrayList.add(trustedCertMock);
+ X509Certificate[] certs = {};
+ EasyMock.expect(request.getAttribute("javax.servlet.request.X509Certificate")).andReturn(certArrayList.toArray(certs)).anyTimes();
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response, trustedCertMock);
+
+ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+ tr.context = context;
+ tr.init();
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(200, retResponse.getStatus());
+
+ // Parse the response
+ String retString = writer.toString();
+ String accessToken = getTagValue(retString, "access_token");
+ assertNotNull(accessToken);
+ String expiry = getTagValue(retString, "expires_in");
+ assertNotNull(expiry);
+
+ // Verify the token
- JWTToken parsedToken = new JWTToken(accessToken);
++ JWT parsedToken = new JWTToken(accessToken);
+ assertEquals("alice", parsedToken.getSubject());
+ assertTrue(authority.verifyToken(parsedToken));
+ }
+
+ @Test
+ public void testValidClientCertWrongUser() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knox.token.client.cert.required")).andReturn("true");
+ EasyMock.expect(context.getInitParameter("knox.token.allowed.principals")).andReturn("CN=remotehost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US");
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+ X509Certificate trustedCertMock = EasyMock.createMock(X509Certificate.class);
+ EasyMock.expect(trustedCertMock.getSubjectDN()).andReturn(new PrimaryPrincipal("CN=localhost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US")).anyTimes();
+ ArrayList<X509Certificate> certArrayList = new ArrayList<X509Certificate>();
+ certArrayList.add(trustedCertMock);
+ X509Certificate[] certs = {};
+ EasyMock.expect(request.getAttribute("javax.servlet.request.X509Certificate")).andReturn(certArrayList.toArray(certs)).anyTimes();
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response, trustedCertMock);
+
+ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+ tr.context = context;
+ tr.init();
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(403, retResponse.getStatus());
+ }
+
+ @Test
+ public void testMissingClientCert() throws Exception {
+
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knox.token.client.cert.required")).andReturn("true");
+ EasyMock.expect(context.getInitParameter("knox.token.allowed.principals")).andReturn("CN=remotehost, OU=Test, O=Hadoop, L=Test, ST=Test, C=US");
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+ EasyMock.expect(request.getAttribute("javax.servlet.request.X509Certificate")).andReturn(null).anyTimes();
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response);
+
+ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+ tr.context = context;
+ tr.init();
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(403, retResponse.getStatus());
+ }
+
++ @Test
++ public void testSignatureAlgorithm() throws Exception {
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++ EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knox.token.sigalg")).andReturn("RS512");
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ StringWriter writer = new StringWriter();
++ PrintWriter printWriter = new PrintWriter(writer);
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++ EasyMock.replay(principal, services, context, request, response);
++
++ TokenResource tr = new TokenResource();
++ tr.request = request;
++ tr.response = response;
++ tr.context = context;
++ tr.init();
++
++ // Issue a token
++ Response retResponse = tr.doGet();
++
++ assertEquals(200, retResponse.getStatus());
++
++ // Parse the response
++ String retString = writer.toString();
++ String accessToken = getTagValue(retString, "access_token");
++ assertNotNull(accessToken);
++ String expiry = getTagValue(retString, "expires_in");
++ assertNotNull(expiry);
++
++ // Verify the token
++ JWT parsedToken = new JWTToken(accessToken);
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++ assertTrue(parsedToken.getHeader().contains("RS512"));
++ }
++
++ @Test
++ public void testDefaultTTL() throws Exception {
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++ EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ StringWriter writer = new StringWriter();
++ PrintWriter printWriter = new PrintWriter(writer);
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++ EasyMock.replay(principal, services, context, request, response);
++
++ TokenResource tr = new TokenResource();
++ tr.request = request;
++ tr.response = response;
++ tr.context = context;
++ tr.init();
++
++ // Issue a token
++ Response retResponse = tr.doGet();
++
++ assertEquals(200, retResponse.getStatus());
++
++ // Parse the response
++ String retString = writer.toString();
++ String accessToken = getTagValue(retString, "access_token");
++ assertNotNull(accessToken);
++ String expiry = getTagValue(retString, "expires_in");
++ assertNotNull(expiry);
++
++ // Verify the token
++ JWT parsedToken = new JWTToken(accessToken);
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++
++ Date expiresDate = parsedToken.getExpiresDate();
++ Date now = new Date();
++ assertTrue(expiresDate.after(now));
++ assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++ }
++
++ @Test
++ public void testCustomTTL() throws Exception {
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++ EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn("60000");
++ EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ StringWriter writer = new StringWriter();
++ PrintWriter printWriter = new PrintWriter(writer);
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++ EasyMock.replay(principal, services, context, request, response);
++
++ TokenResource tr = new TokenResource();
++ tr.request = request;
++ tr.response = response;
++ tr.context = context;
++ tr.init();
++
++ // Issue a token
++ Response retResponse = tr.doGet();
++
++ assertEquals(200, retResponse.getStatus());
++
++ // Parse the response
++ String retString = writer.toString();
++ String accessToken = getTagValue(retString, "access_token");
++ assertNotNull(accessToken);
++ String expiry = getTagValue(retString, "expires_in");
++ assertNotNull(expiry);
++
++ // Verify the token
++ JWT parsedToken = new JWTToken(accessToken);
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++
++ Date expiresDate = parsedToken.getExpiresDate();
++ Date now = new Date();
++ assertTrue(expiresDate.after(now));
++ long diff = expiresDate.getTime() - now.getTime();
++ assertTrue(diff < 60000L && diff > 30000L);
++ }
++
++ @Test
++ public void testNegativeTTL() throws Exception {
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++ EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn("-60000");
++ EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ StringWriter writer = new StringWriter();
++ PrintWriter printWriter = new PrintWriter(writer);
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++ EasyMock.replay(principal, services, context, request, response);
++
++ TokenResource tr = new TokenResource();
++ tr.request = request;
++ tr.response = response;
++ tr.context = context;
++ tr.init();
++
++ // Issue a token
++ Response retResponse = tr.doGet();
++
++ assertEquals(200, retResponse.getStatus());
++
++ // Parse the response
++ String retString = writer.toString();
++ String accessToken = getTagValue(retString, "access_token");
++ assertNotNull(accessToken);
++ String expiry = getTagValue(retString, "expires_in");
++ assertNotNull(expiry);
++
++ // Verify the token
++ JWT parsedToken = new JWTToken(accessToken);
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++
++ Date expiresDate = parsedToken.getExpiresDate();
++ Date now = new Date();
++ assertTrue(expiresDate.after(now));
++ assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++ }
++
++ @Test
++ public void testOverflowTTL() throws Exception {
++ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
++ EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
++ EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(String.valueOf(Long.MAX_VALUE));
++ EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
++ EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
++
++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
++ Principal principal = EasyMock.createNiceMock(Principal.class);
++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
++
++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
++
++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
++
++ StringWriter writer = new StringWriter();
++ PrintWriter printWriter = new PrintWriter(writer);
++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++ EasyMock.expect(response.getWriter()).andReturn(printWriter);
++
++ EasyMock.replay(principal, services, context, request, response);
++
++ TokenResource tr = new TokenResource();
++ tr.request = request;
++ tr.response = response;
++ tr.context = context;
++ tr.init();
++
++ // Issue a token
++ Response retResponse = tr.doGet();
++
++ assertEquals(200, retResponse.getStatus());
++
++ // Parse the response
++ String retString = writer.toString();
++ String accessToken = getTagValue(retString, "access_token");
++ assertNotNull(accessToken);
++ String expiry = getTagValue(retString, "expires_in");
++ assertNotNull(expiry);
++
++ // Verify the token
++ JWT parsedToken = new JWTToken(accessToken);
++ assertEquals("alice", parsedToken.getSubject());
++ assertTrue(authority.verifyToken(parsedToken));
++
++ Date expiresDate = parsedToken.getExpiresDate();
++ Date now = new Date();
++ assertTrue(expiresDate.after(now));
++ assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
++ }
++
+ private String getTagValue(String token, String tagName) {
+ String searchString = tagName + "\":";
+ String value = token.substring(token.indexOf(searchString) + searchString.length());
+ if (value.startsWith("\"")) {
+ value = value.substring(1);
+ }
+ if (value.contains("\"")) {
+ return value.substring(0, value.indexOf("\""));
+ } else if (value.contains(",")) {
+ return value.substring(0, value.indexOf(","));
+ } else {
+ return value.substring(0, value.length() - 1);
+ }
+ }
+
+ private static class TestJWTokenAuthority implements JWTokenAuthority {
+
+ private RSAPublicKey publicKey;
+ private RSAPrivateKey privateKey;
+
+ public TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
+ this.publicKey = publicKey;
+ this.privateKey = privateKey;
+ }
+
+ @Override
+ public JWT issueToken(Subject subject, String algorithm)
+ throws TokenServiceException {
+ Principal p = (Principal) subject.getPrincipals().toArray()[0];
+ return issueToken(p, algorithm);
+ }
+
+ @Override
+ public JWT issueToken(Principal p, String algorithm)
+ throws TokenServiceException {
+ return issueToken(p, null, algorithm);
+ }
+
+ @Override
+ public JWT issueToken(Principal p, String audience, String algorithm)
+ throws TokenServiceException {
+ return issueToken(p, audience, algorithm, -1);
+ }
+
+ @Override
+ public boolean verifyToken(JWT token) throws TokenServiceException {
+ JWSVerifier verifier = new RSASSAVerifier(publicKey);
+ return token.verify(verifier);
+ }
+
+ @Override
+ public JWT issueToken(Principal p, String audience, String algorithm,
+ long expires) throws TokenServiceException {
+ ArrayList<String> audiences = null;
+ if (audience != null) {
+ audiences = new ArrayList<String>();
+ audiences.add(audience);
+ }
+ return issueToken(p, audiences, algorithm, expires);
+ }
+
+ @Override
+ public JWT issueToken(Principal p, List<String> audiences, String algorithm,
+ long expires) throws TokenServiceException {
+ String[] claimArray = new String[4];
+ claimArray[0] = "KNOXSSO";
+ claimArray[1] = p.getName();
+ claimArray[2] = null;
+ if (expires == -1) {
+ claimArray[3] = null;
+ } else {
+ claimArray[3] = String.valueOf(expires);
+ }
+
- JWTToken token = null;
- if ("RS256".equals(algorithm)) {
- token = new JWTToken("RS256", claimArray, audiences);
- JWSSigner signer = new RSASSASigner(privateKey);
- token.sign(signer);
- } else {
- throw new TokenServiceException("Cannot issue token - Unsupported algorithm");
- }
++ JWT token = new JWTToken(algorithm, claimArray, audiences);
++ JWSSigner signer = new RSASSASigner(privateKey);
++ token.sign(signer);
+
+ return token;
+ }
+
+ @Override
+ public JWT issueToken(Principal p, String algorithm, long expiry)
+ throws TokenServiceException {
+ return issueToken(p, Collections.<String>emptyList(), algorithm, expiry);
+ }
+
+ @Override
+ public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException {
+ JWSVerifier verifier = new RSASSAVerifier(publicKey);
+ return token.verify(verifier);
+ }
+
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-shell/src/main/java/org/apache/knox/gateway/shell/job/Sqoop.java
----------------------------------------------------------------------
diff --cc gateway-shell/src/main/java/org/apache/knox/gateway/shell/job/Sqoop.java
index ec9f907,0000000..072b58d
mode 100644,000000..100644
--- a/gateway-shell/src/main/java/org/apache/knox/gateway/shell/job/Sqoop.java
+++ b/gateway-shell/src/main/java/org/apache/knox/gateway/shell/job/Sqoop.java
@@@ -1,99 -1,0 +1,99 @@@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.shell.job;
+
+import com.jayway.jsonpath.JsonPath;
+import org.apache.knox.gateway.shell.AbstractRequest;
+import org.apache.knox.gateway.shell.BasicResponse;
+import org.apache.knox.gateway.shell.Hadoop;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+public class Sqoop {
+
- static class Request extends AbstractRequest<Response> {
++ public static class Request extends AbstractRequest<Response> {
+
+ private String statusDir;
+ List<NameValuePair> params = new ArrayList<NameValuePair>();
+
+ public Request( Hadoop session ) {
+ super( session );
+ }
+
+ public Request command( String command ) {
+ addParam( params, "command", command );
+ return this;
+ }
+
+ public Request libdir( String libdir ) {
+ addParam( params, "libdir", libdir );
+ return this;
+ }
+
+ public Request files( String files ) {
+ addParam( params, "files", files );
+ return this;
+ }
+
+ public Request optionsfile( String optionsFile ) {
+ addParam( params, "optionsfile", optionsFile );
+ return this;
+ }
+
+ public Request statusDir( String dir ) {
+ this.statusDir = dir;
+ return this;
+ }
+
+ protected Callable<Response> callable() {
+ return new Callable<Response>() {
+ @Override
+ public Response call() throws Exception {
+ URIBuilder uri = uri( Job.SERVICE_PATH, "/sqoop" );
+ addParam( params, "statusdir", statusDir );
+ UrlEncodedFormEntity form = new UrlEncodedFormEntity( params );
+ HttpPost request = new HttpPost( uri.build() );
+ request.setEntity( form );
+ return new Response( execute( request ) );
+ }
+ };
+ }
+
+ }
+
+ public static class Response extends BasicResponse {
+
+ protected Response( HttpResponse response ) {
+ super( response );
+ }
+
+ public String getJobId() throws IOException {
+ return JsonPath.read( getString(), "$.id" );
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWT.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWT.java
index 8638da5,0000000..bd7cd5c
mode 100644,000000..100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWT.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWT.java
@@@ -1,63 -1,0 +1,66 @@@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.services.security.token.impl;
+
+import java.util.Date;
+
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.JWSVerifier;
+
+public interface JWT {
+
+ String PRINCIPAL = "prn";
+ String SUBJECT = "sub";
+ String ISSUER = "iss";
+ String AUDIENCE = "aud";
+ String EXPIRES = "exp";
++ String NOT_BEFORE = "nbf";
+
+ String getPayload();
+
+ void setSignaturePayload(byte[] payload);
+
+ byte[] getSignaturePayload();
+
+ String getClaim(String claimName);
+
+ String getPrincipal();
+
+ String getIssuer();
+
+ String getAudience();
+
+ public String[] getAudienceClaims();
+
+ String getExpires();
+
+ Date getExpiresDate();
+
++ Date getNotBeforeDate();
++
+ String getSubject();
+
+ String getHeader();
+
+ String getClaims();
+
+ void sign(JWSSigner signer);
+
+ boolean verify(JWSVerifier verifier);
+
+}