You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by lm...@apache.org on 2017/01/26 22:35:07 UTC

knox git commit: POC commit to feature branch

Repository: knox
Updated Branches:
  refs/heads/knoxinit [created] d6fda9c8d


POC commit to feature branch

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

Branch: refs/heads/knoxinit
Commit: d6fda9c8d6b8d51f931dd715a4792c000820051f
Parents: ebc2ac8
Author: Larry McCay <lm...@hortonworks.com>
Authored: Thu Jan 26 17:34:09 2017 -0500
Committer: Larry McCay <lm...@hortonworks.com>
Committed: Thu Jan 26 17:34:09 2017 -0500

----------------------------------------------------------------------
 .../jwt/filter/JWTFederationFilter.java         |  29 ++--
 gateway-release/pom.xml                         |   4 +
 gateway-service-knoxtoken/pom.xml               |  67 +++++++++
 .../service/knoxtoken/TokenResource.java        | 149 +++++++++++++++++++
 .../service/knoxtoken/TokenServiceMessages.java |  66 ++++++++
 .../TokenServiceDeploymentContributor.java      |  55 +++++++
 ....gateway.deploy.ServiceDeploymentContributor |  19 +++
 .../service/knoxsso/WebSSOResourceTest.java     |  71 +++++++++
 .../hadoop/gateway/shell/AbstractRequest.java   |  22 +++
 .../org/apache/hadoop/gateway/shell/Hadoop.java |  86 +++++++++++
 .../shell/KnoxTokenCredentialCollector.java     |  75 ++++++++++
 .../hadoop/gateway/shell/knox/token/Get.java    |  57 +++++++
 .../hadoop/gateway/shell/knox/token/Token.java  |  29 ++++
 ...che.hadoop.gateway.shell.CredentialCollector |   3 +-
 pom.xml                                         |   6 +
 15 files changed, 722 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
index 48bc51d..9a95421 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
@@ -20,6 +20,7 @@ package org.apache.hadoop.gateway.provider.federation.jwt.filter;
 import org.apache.commons.logging.Log;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
 import org.apache.hadoop.gateway.provider.federation.jwt.JWTMessages;
+import org.apache.hadoop.gateway.security.PrimaryPrincipal;
 import org.apache.hadoop.gateway.services.GatewayServices;
 import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
 import org.apache.hadoop.gateway.services.security.token.TokenServiceException;
@@ -65,11 +66,12 @@ public class JWTFederationFilter implements Filter {
       // what follows the bearer designator should be the JWT token being used to request or as an access token
       String wireToken = header.substring(BEARER.length());
       JWTToken token;
-      try {
-        token = JWTToken.parseToken(wireToken);
-      } catch (ParseException e) {
-        throw new ServletException("ParseException encountered while processing the JWT token: ", e);
-      }
+//      try {
+        token = new JWTToken(wireToken);
+//        token = JWTToken.parseToken(wireToken);
+//      } catch (ParseException e) {
+//        throw new ServletException("ParseException encountered while processing the JWT token: ", e);
+//      }
       boolean verified = false;
       try {
         verified = authority.verifyToken(token);
@@ -78,8 +80,8 @@ public class JWTFederationFilter implements Filter {
       }
       if (verified) {
         // TODO: validate expiration
-        // confirm that audience matches intended target - which for this filter must be HSSO
-        if (token.getAudience().equals("HSSO")) {
+        // confirm that audience matches intended target - which for this filter must be KNOXSSO
+        if (token.getIssuer().equals("KNOXSSO")) {
           // TODO: verify that the user requesting access to the service/resource is authorized for it - need scopes?
           Subject subject = createSubjectFromToken(token);
           continueWithEstablishedSecurityContext(subject, (HttpServletRequest)request, (HttpServletResponse)response, chain);
@@ -130,26 +132,23 @@ public class JWTFederationFilter implements Filter {
   }
   
   private Subject createSubjectFromToken(JWTToken token) {
-    final String principal = token.getPrincipal();
+    final String principal = token.getSubject();
 
+    @SuppressWarnings("rawtypes")
     HashSet emptySet = new HashSet();
     Set<Principal> principals = new HashSet<Principal>();
-    Principal p = new Principal() {
-      @Override
-      public String getName() {
-        return principal;
-      }
-    };
+    Principal p = new PrimaryPrincipal(principal);
     principals.add(p);
     
 //        The newly constructed Sets check whether this Subject has been set read-only 
 //        before permitting subsequent modifications. The newly created Sets also prevent 
 //        illegal modifications by ensuring that callers have sufficient permissions.
- //
+//
 //        To modify the Principals Set, the caller must have AuthPermission("modifyPrincipals"). 
 //        To modify the public credential Set, the caller must have AuthPermission("modifyPublicCredentials"). 
 //        To modify the private credential Set, the caller must have AuthPermission("modifyPrivateCredentials").
     javax.security.auth.Subject subject = new javax.security.auth.Subject(true, principals, emptySet, emptySet);
     return subject;
   }
+
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-release/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-release/pom.xml b/gateway-release/pom.xml
index 5b3e7c1..09c38fd 100644
--- a/gateway-release/pom.xml
+++ b/gateway-release/pom.xml
@@ -196,6 +196,10 @@
         </dependency>
         <dependency>
             <groupId>${gateway-group}</groupId>
+            <artifactId>gateway-service-knoxtoken</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${gateway-group}</groupId>
             <artifactId>gateway-provider-rewrite</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-service-knoxtoken/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/pom.xml b/gateway-service-knoxtoken/pom.xml
new file mode 100644
index 0000000..7c1d805
--- /dev/null
+++ b/gateway-service-knoxtoken/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.knox</groupId>
+    <artifactId>gateway</artifactId>
+    <version>0.12.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.knox</groupId>
+  <artifactId>gateway-service-knoxtoken</artifactId>
+  <version>0.12.0-SNAPSHOT</version>
+  <name>gateway-service-knoxtoken</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+  <dependencies>
+	  <dependency>
+	    <groupId>${gateway-group}</groupId>
+	    <artifactId>gateway-util-common</artifactId>
+	  </dependency>
+	  <dependency>
+	    <groupId>${gateway-group}</groupId>
+	    <artifactId>gateway-spi</artifactId>
+	  </dependency>
+	  <dependency>
+	    <groupId>${gateway-group}</groupId>
+	    <artifactId>gateway-provider-rewrite</artifactId>
+	  </dependency>
+	  <dependency>
+	    <groupId>${gateway-group}</groupId>
+	    <artifactId>gateway-provider-jersey</artifactId>
+	  </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.knox</groupId>
+      <artifactId>gateway-test-utils</artifactId>
+      <scope>test</scope>
+    </dependency>
+      <dependency>
+          <groupId>org.easymock</groupId>
+          <artifactId>easymock</artifactId>
+          <scope>test</scope>
+      </dependency>  </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
new file mode 100644
index 0000000..0d7688a
--- /dev/null
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
@@ -0,0 +1,149 @@
+/**
+ * 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.hadoop.gateway.service.knoxtoken;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+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.hadoop.gateway.i18n.messages.MessagesFactory;
+import org.apache.hadoop.gateway.services.GatewayServices;
+import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
+import org.apache.hadoop.gateway.services.security.token.TokenServiceException;
+import org.apache.hadoop.gateway.services.security.token.impl.JWT;
+import org.apache.hadoop.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 BEARER = "Bearer ";
+  private static final String TOKEN_TTL_PARAM = "knox.token.ttl";
+  private static final String TOKEN_AUDIENCES_PARAM = "knox.token.audiences";
+  static final String RESOURCE_PATH = "knoxtoken/api/v1/token";
+  private static TokenServiceMessages log = MessagesFactory.get( TokenServiceMessages.class );
+  private long tokenTTL = 30000l;
+  private String[] targetAudiences = null;
+
+  @Context
+  private HttpServletRequest request;
+
+  @Context
+  private HttpServletResponse response;
+
+  @Context
+  ServletContext context;
+
+  @PostConstruct
+  public void init() {
+
+    String audiences = context.getInitParameter(TOKEN_AUDIENCES_PARAM);
+    if (audiences != null) {
+      targetAudiences = audiences.split(",");
+    }
+
+    String ttl = context.getInitParameter(TOKEN_TTL_PARAM);
+    if (ttl != null) {
+      try {
+        tokenTTL = Long.parseLong(ttl);
+      }
+      catch (NumberFormatException nfe) {
+        log.invalidTokenTTLEncountered(ttl);
+      }
+    }
+  }
+
+  @GET
+  @Produces({APPLICATION_JSON, APPLICATION_XML})
+  public Response doGet() {
+    return getAuthenticationToken();
+  }
+
+  @POST
+  @Produces({APPLICATION_JSON, APPLICATION_XML})
+  public Response doPost() {
+    return getAuthenticationToken();
+  }
+
+  private Response getAuthenticationToken() {
+    GatewayServices services = (GatewayServices) request.getServletContext()
+            .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
+
+    JWTokenAuthority ts = services.getService(GatewayServices.TOKEN_SERVICE);
+    Principal p = ((HttpServletRequest)request).getUserPrincipal();
+    long expires = getExpiry();
+    
+    try {
+      JWT token = null;
+      if (targetAudiences == null || targetAudiences.length == 0) {
+        token = ts.issueToken(p, "RS256", getExpiry());
+      } else {
+        ArrayList<String> aud = new ArrayList<String>();
+        for (int i = 0; i < targetAudiences.length; i++) {
+          aud.add(targetAudiences[i]);
+        }
+        token = ts.issueToken(p, aud, "RS256", expires);
+      }
+
+      String accessToken = token.toString();
+
+      HashMap<String, Object> map = new HashMap<String, Object>();
+      // TODO: populate map from JWT authorization code
+      map.put(ACCESS_TOKEN, accessToken);
+      map.put(TOKEN_TYPE, BEARER);
+      map.put(EXPIRES_IN, expires);
+      
+      String jsonResponse = JsonUtils.renderAsJsonString(map);
+      
+      response.getWriter().write(jsonResponse);
+      //KNOX-685: response.getWriter().flush();
+//      return response; // break filter chain
+      return Response.ok().build();
+
+    }
+    catch (TokenServiceException | IOException e) {
+      log.unableToIssueToken(e);
+    }
+
+    return Response.ok().entity("{ \"Unable to acquire token.\" }").build();
+  }
+
+  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/d6fda9c8/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceMessages.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceMessages.java b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceMessages.java
new file mode 100644
index 0000000..b590614
--- /dev/null
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceMessages.java
@@ -0,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.hadoop.gateway.service.knoxtoken;
+
+import org.apache.hadoop.gateway.i18n.messages.Message;
+import org.apache.hadoop.gateway.i18n.messages.MessageLevel;
+import org.apache.hadoop.gateway.i18n.messages.Messages;
+import org.apache.hadoop.gateway.i18n.messages.StackTrace;
+
+@Messages(logger="org.apache.hadoop.gateway.service.knoxsso")
+public interface TokenServiceMessages {
+  @Message( level = MessageLevel.INFO, text = "About to redirect to original URL: {0}")
+  void aboutToRedirectToOriginal(String original);
+
+  @Message( level = MessageLevel.DEBUG, text = "Adding the following JWT token as a cookie: {0}")
+  void addingJWTCookie(String token);
+
+  @Message( level = MessageLevel.INFO, text = "Unable to find cookie with name: {0}")
+  void cookieNotFound(String name);
+
+  @Message( level = MessageLevel.ERROR, text = "Unable to properly send needed HTTP status code: {0}, {1}")
+  void unableToCloseOutputStream(String message, String string);
+
+  @Message( level = MessageLevel.ERROR, text = "Unable to add cookie to response. {0}: {1}")
+  void unableAddCookieToResponse(String message, String stackTrace);
+
+  @Message( level = MessageLevel.ERROR, text = "Original URL not found in request.")
+  void originalURLNotFound();
+
+  @Message( level = MessageLevel.INFO, text = "JWT cookie successfully added.")
+  void addedJWTCookie();
+
+  @Message( level = MessageLevel.ERROR, text = "Unable to issue token.")
+  void unableToIssueToken(@StackTrace( level = MessageLevel.DEBUG) Exception e);
+
+  @Message( level = MessageLevel.WARN, text = "The SSO cookie SecureOnly flag is set to FALSE and is therefore insecure.")
+  void cookieSecureOnly(boolean secureOnly);
+
+  @Message( level = MessageLevel.WARN, text = "The SSO cookie max age configuration is invalid: {0} - using default.")
+  void invalidMaxAgeEncountered(String age);
+
+  @Message( level = MessageLevel.WARN, text = "The SSO token time to live - ttl is invalid: {0} - using default.")
+  void invalidTokenTTLEncountered(String ttl);
+
+  @Message( level = MessageLevel.INFO, text = "The cookie max age is being set to: {0}.")
+  void setMaxAge(String age);
+
+  @Message( level = MessageLevel.ERROR, text = "The original URL: {0} for redirecting back after authentication is " +
+  		"not valid according to the configured whitelist: {1}. See documentation for KnoxSSO Whitelisting.")
+  void whiteListMatchFail(String original, String whitelist);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/deploy/TokenServiceDeploymentContributor.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/deploy/TokenServiceDeploymentContributor.java b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/deploy/TokenServiceDeploymentContributor.java
new file mode 100644
index 0000000..cc92732
--- /dev/null
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/deploy/TokenServiceDeploymentContributor.java
@@ -0,0 +1,55 @@
+/**
+ * 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.hadoop.gateway.service.knoxtoken.deploy;
+
+import org.apache.hadoop.gateway.jersey.JerseyServiceDeploymentContributorBase;
+
+public class TokenServiceDeploymentContributor extends JerseyServiceDeploymentContributorBase {
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.deploy.ServiceDeploymentContributor#getRole()
+   */
+  @Override
+  public String getRole() {
+    return "KNOXTOKEN";
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.deploy.ServiceDeploymentContributor#getName()
+   */
+  @Override
+  public String getName() {
+    return "KnoxTokenService";
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.jersey.JerseyServiceDeploymentContributorBase#getPackages()
+   */
+  @Override
+  protected String[] getPackages() {
+    return new String[]{ "org.apache.hadoop.gateway.service.knoxtoken" };
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.jersey.JerseyServiceDeploymentContributorBase#getPatterns()
+   */
+  @Override
+  protected String[] getPatterns() {
+    return new String[]{ "knoxtoken/api/**?**" };
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-service-knoxtoken/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ServiceDeploymentContributor
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ServiceDeploymentContributor b/gateway-service-knoxtoken/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ServiceDeploymentContributor
new file mode 100644
index 0000000..d250459
--- /dev/null
+++ b/gateway-service-knoxtoken/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ServiceDeploymentContributor
@@ -0,0 +1,19 @@
+##########################################################################
+# 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.
+##########################################################################
+
+org.apache.hadoop.gateway.service.knoxtoken.deploy.TokenServiceDeploymentContributor
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
new file mode 100644
index 0000000..73910dd
--- /dev/null
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
@@ -0,0 +1,71 @@
+/**
+ * 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.hadoop.gateway.service.knoxsso;
+
+import org.apache.hadoop.gateway.util.RegExUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class WebSSOResourceTest {
+
+  @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/"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/AbstractRequest.java
----------------------------------------------------------------------
diff --git a/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/AbstractRequest.java b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/AbstractRequest.java
index 67ee7ad..4c5dfb3 100644
--- a/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/AbstractRequest.java
+++ b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/AbstractRequest.java
@@ -28,6 +28,7 @@ import org.apache.http.message.BasicNameValuePair;
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
 
@@ -44,9 +45,30 @@ public abstract class AbstractRequest<T> {
   }
 
   protected CloseableHttpResponse execute(HttpRequest request ) throws IOException {
+    addHeaders(request, session.getHeaders());
     return session.executeNow( request );
   }
 
+  /**
+   * @param request
+   * @param headers
+   */
+  private void removeHeaders(HttpRequest request, Map<String, String> headers) {
+    for(String header : headers.keySet()) {
+      request.removeHeaders(header);
+    }
+  }
+
+  /**
+   * @param request
+   * @param headers
+   */
+  private void addHeaders(HttpRequest request, Map<String, String> headers) {
+    for(String header : headers.keySet()) {
+      request.setHeader(header, headers.get(header));
+    }
+  }
+
   protected URIBuilder uri( String... parts ) throws URISyntaxException {
     return new URIBuilder( session.base() + StringUtils.join( parts ) );
   }

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/Hadoop.java
----------------------------------------------------------------------
diff --git a/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/Hadoop.java b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/Hadoop.java
index 1fe28b1..b2e186f 100644
--- a/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/Hadoop.java
+++ b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/Hadoop.java
@@ -59,6 +59,8 @@ import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateException;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -80,6 +82,15 @@ public class Hadoop implements Closeable {
   CloseableHttpClient client;
   BasicHttpContext context;
   ExecutorService executor;
+  Map<String, String> headers = new HashMap<String, String>();
+
+  public Map<String, String> getHeaders() {
+    return headers;
+  }
+
+  public void setHeaders(Map<String, String> headers) {
+    this.headers = headers;
+  }
 
   public static Hadoop login( String url, String username, String password ) throws URISyntaxException {
     return new Hadoop(ClientContext.with(username, password, url));
@@ -90,11 +101,29 @@ public class Hadoop implements Closeable {
             .connection().secure(false).end());
   }
 
+<<<<<<< Updated upstream
   public Hadoop( ClientContext clientContext) throws HadoopException, URISyntaxException {
+=======
+  public static Hadoop login(String url, Map<String, String> headers) throws URISyntaxException {
+    return new Hadoop( url, headers, true );
+  }
+
+  private Hadoop( String url, Map<String, String> headers ) throws HadoopException, URISyntaxException {
+    this(url, null, null, false);
+    this.headers = headers;
+  }
+
+  private Hadoop( String url, String username, String password ) throws HadoopException, URISyntaxException {
+    this(url, username, password, false);
+  }
+
+  private Hadoop( String url, String username, String password, boolean secure ) throws HadoopException, URISyntaxException {
+>>>>>>> Stashed changes
     this.executor = Executors.newCachedThreadPool();
     this.base = clientContext.url();
 
     try {
+<<<<<<< Updated upstream
       client = createClient(clientContext);
     } catch (GeneralSecurityException e) {
       throw new HadoopException("Failed to create HTTP client.", e);
@@ -118,6 +147,63 @@ public class Hadoop implements Closeable {
               + "*******************************************");
     }
 
+=======
+      if (!secure) {
+        client = createInsecureClient();
+      }
+      else {
+        client = createClient();
+      }
+      if (username != null && password != null) {
+        client.getCredentialsProvider().setCredentials(
+            new AuthScope( host.getHostName(), host.getPort() ),
+            new UsernamePasswordCredentials( username, password ) );
+        AuthCache authCache = new BasicAuthCache();
+        BasicScheme authScheme = new BasicScheme();
+        authCache.put( host, authScheme );
+        context = new BasicHttpContext();
+        context.setAttribute( ClientContext.AUTH_CACHE, authCache );
+      }
+    } catch( GeneralSecurityException e ) {
+      throw new HadoopException( "Failed to create HTTP client.", e );
+    }
+  }
+
+  private Hadoop(String url, Map<String,String> headers, boolean secure)
+      throws HadoopException, URISyntaxException {
+    this.executor = Executors.newCachedThreadPool();
+    this.base = url;
+    this.headers = headers;
+
+    URI uri = new URI( url );
+    host = new HttpHost( uri.getHost(), uri.getPort(), uri.getScheme() );
+
+    try {
+      if (!secure) {
+        client = createInsecureClient();
+      }
+      else {
+        client = createClient();
+      }
+      if (username != null && password != null) {
+        client.getCredentialsProvider().setCredentials(
+            new AuthScope( host.getHostName(), host.getPort() ),
+            new UsernamePasswordCredentials( username, password ) );
+        AuthCache authCache = new BasicAuthCache();
+        BasicScheme authScheme = new BasicScheme();
+        authCache.put( host, authScheme );
+        context = new BasicHttpContext();
+        context.setAttribute( ClientContext.AUTH_CACHE, authCache );
+      }
+    } catch( GeneralSecurityException e ) {
+      throw new HadoopException( "Failed to create HTTP client.", e );
+    }
+  }
+
+
+  private static DefaultHttpClient createClient() throws GeneralSecurityException {
+    SchemeRegistry registry = new SchemeRegistry();
+>>>>>>> Stashed changes
     KeyStore trustStore = getTrustStore();
     SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore, trustStrategy).build();
     Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/KnoxTokenCredentialCollector.java
----------------------------------------------------------------------
diff --git a/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/KnoxTokenCredentialCollector.java b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/KnoxTokenCredentialCollector.java
new file mode 100644
index 0000000..9972da2
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/KnoxTokenCredentialCollector.java
@@ -0,0 +1,75 @@
+/**
+ * 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.hadoop.gateway.shell;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.hadoop.gateway.util.JsonUtils;
+
+public class KnoxTokenCredentialCollector extends AbstractCredentialCollector {
+  /**
+   * 
+   */
+  private static final String KNOXTOKENCACHE = ".knoxtokencache";
+  public static final String COLLECTOR_TYPE = "KnoxToken";
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.shell.CredentialCollector#collect()
+   */
+  @Override
+  public void collect() throws CredentialCollectionException {
+    String userDir = System.getProperty("user.home");
+    File knoxtoken = new File(userDir, KNOXTOKENCACHE);
+    if (knoxtoken.exists()) {
+      Path path = Paths.get(knoxtoken.toURI());
+      List<String> lines;
+      try {
+        lines = Files.readAllLines(path, StandardCharsets.UTF_8);
+        Map<String, String> attrs = JsonUtils.getMapFromJsonString(lines.get(0));
+        value = attrs.get("access_token");
+        Date expires = new Date(Long.parseLong(attrs.get("expires_in")));
+        if (expires.before(new Date())) {
+          System.out.println("Cached knox token has expired. Please relogin through knoxinit.");
+          System.exit(1);
+        }
+      } catch (IOException e) {
+        System.out.println("Cached knox token cannot be read. Please login through knoxinit.");
+        System.exit(1);
+        e.printStackTrace();
+      }
+    } else {
+      System.out.println("Cached knox token cannot be found. Please login through knoxinit.");
+      System.exit(1);
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.shell.CredentialCollector#name()
+   */
+  @Override
+  public String type() {
+    return COLLECTOR_TYPE;
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/knox/token/Get.java
----------------------------------------------------------------------
diff --git a/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/knox/token/Get.java b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/knox/token/Get.java
new file mode 100644
index 0000000..f946db5
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/knox/token/Get.java
@@ -0,0 +1,57 @@
+/**
+ * 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.hadoop.gateway.shell.knox.token;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+
+import org.apache.hadoop.gateway.shell.AbstractRequest;
+import org.apache.hadoop.gateway.shell.BasicResponse;
+import org.apache.hadoop.gateway.shell.Hadoop;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.utils.URIBuilder;
+
+/**
+ * Acquire a Knox access token for token based authentication
+ * to access REST APIs
+ */
+public class Get {
+  public static class Request extends AbstractRequest<Response> {
+    Request(Hadoop session) {
+      super(session);
+    }
+
+    protected Callable<Response> callable() {
+      return new Callable<Response>() {
+        @Override
+        public Response call() throws Exception {
+          URIBuilder uri = uri(Token.SERVICE_PATH);
+          HttpGet request = new HttpGet(uri.build());
+          return new Response(execute(request));
+        }
+      };
+    }
+  }
+
+  public static class Response extends BasicResponse {
+    Response(HttpResponse response) throws IOException {
+      super(response);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/knox/token/Token.java
----------------------------------------------------------------------
diff --git a/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/knox/token/Token.java b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/knox/token/Token.java
new file mode 100644
index 0000000..b7f5115
--- /dev/null
+++ b/gateway-shell/src/main/java/org/apache/hadoop/gateway/shell/knox/token/Token.java
@@ -0,0 +1,29 @@
+/**
+ * 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.hadoop.gateway.shell.knox.token;
+
+import org.apache.hadoop.gateway.shell.Hadoop;
+
+public class Token {
+
+  static String SERVICE_PATH = "/knoxtoken/api/v1/token";
+
+  public static Get.Request get( Hadoop session ) {
+    return new Get.Request( session );
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/gateway-shell/src/main/resources/META-INF/services/org.apache.hadoop.gateway.shell.CredentialCollector
----------------------------------------------------------------------
diff --git a/gateway-shell/src/main/resources/META-INF/services/org.apache.hadoop.gateway.shell.CredentialCollector b/gateway-shell/src/main/resources/META-INF/services/org.apache.hadoop.gateway.shell.CredentialCollector
index eb6d5b8..e4f8462 100644
--- a/gateway-shell/src/main/resources/META-INF/services/org.apache.hadoop.gateway.shell.CredentialCollector
+++ b/gateway-shell/src/main/resources/META-INF/services/org.apache.hadoop.gateway.shell.CredentialCollector
@@ -17,4 +17,5 @@
 ##########################################################################
 
 org.apache.hadoop.gateway.shell.ClearInputCredentialCollector
-org.apache.hadoop.gateway.shell.HiddenInputCredentialCollector
\ No newline at end of file
+org.apache.hadoop.gateway.shell.HiddenInputCredentialCollector
+org.apache.hadoop.gateway.shell.KnoxTokenCredentialCollector
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/d6fda9c8/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 43ad583..c317165 100644
--- a/pom.xml
+++ b/pom.xml
@@ -76,6 +76,7 @@
         <module>gateway-service-hive</module>
         <module>gateway-service-knoxsso</module>
         <module>gateway-service-knoxssout</module>
+        <module>gateway-service-knoxtoken</module>
         <module>gateway-service-webhdfs</module>
         <module>gateway-service-tgs</module>
         <module>gateway-service-storm</module>
@@ -582,6 +583,11 @@
             </dependency>
             <dependency>
                 <groupId>${gateway-group}</groupId>
+                <artifactId>gateway-service-knoxtoken</artifactId>
+                <version>${gateway-version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${gateway-group}</groupId>
                 <artifactId>gateway-service-admin</artifactId>
                 <version>${gateway-version}</version>
             </dependency>