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 2013/04/16 20:00:04 UTC

git commit: Another iteration of work toward KNOX-37 - POC work for demonstrating an access token issuance and acceptance for SSO.

Updated Branches:
  refs/heads/master ae2d31c7a -> d205789ab


Another iteration of work toward KNOX-37 - POC work for demonstrating an access token issuance and acceptance for SSO.

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

Branch: refs/heads/master
Commit: d205789ab938db23a5c9fe806bd261d2a7dd8acc
Parents: ae2d31c
Author: Larry McCay <lm...@hortonworks.com>
Authored: Tue Apr 16 13:59:18 2013 -0400
Committer: Larry McCay <lm...@hortonworks.com>
Committed: Tue Apr 16 13:59:18 2013 -0400

----------------------------------------------------------------------
 .../provider/federation/jwt/JWTAuthority.java      |    5 +-
 .../deploy/AccessTokenFederationContributor.java   |   51 ++++++
 .../jwt/filter/AccessTokenFederationFilter.java    |  136 +++++++++++++++
 .../jwt/filter/JWTAuthCodeAssertionFilter.java     |   47 +++++
 ...op.gateway.deploy.ProviderDeploymentContributor |    4 +-
 gateway-release/home/deployments/sample.xml        |    6 +
 gateway-release/home/templates/topology.xml        |    6 +
 .../security/impl/DefaultCryptoService.java        |    4 +-
 .../security/impl/DefaultKeystoreService.java      |    4 +-
 .../gateway/services/security/KeystoreService.java |    2 +-
 10 files changed, 258 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/d205789a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTAuthority.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTAuthority.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTAuthority.java
index 9c34773..34c3713 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTAuthority.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTAuthority.java
@@ -21,6 +21,7 @@ import java.security.Principal;
 
 import javax.security.auth.Subject;
 
+import org.apache.hadoop.gateway.services.security.AliasService;
 import org.apache.hadoop.gateway.services.security.CryptoService;
 
 public class JWTAuthority {
@@ -30,7 +31,7 @@ public class JWTAuthority {
     this.crypto = crypto;
   }
   
-  public JWTToken issueToken(Subject subject, String algorithm, byte[] secret) {
+  public JWTToken issueToken(Subject subject, String algorithm) {
     Principal p = (Principal) subject.getPrincipals().toArray()[0];
     String[] claimArray = new String[4];
     claimArray[0] = "gateway";
@@ -42,7 +43,7 @@ public class JWTAuthority {
 
     JWTToken token = null;
     if ("RS256".equals(algorithm)) {
-      new JWTToken("RS256", claimArray);
+      token = new JWTToken("RS256", claimArray);
       signToken(token);
     }
     else {

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/d205789a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/AccessTokenFederationContributor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/AccessTokenFederationContributor.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/AccessTokenFederationContributor.java
new file mode 100644
index 0000000..2245336
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/AccessTokenFederationContributor.java
@@ -0,0 +1,51 @@
+/**
+ * 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.provider.federation.jwt.deploy;
+
+import org.apache.hadoop.gateway.deploy.DeploymentContext;
+import org.apache.hadoop.gateway.deploy.ProviderDeploymentContributorBase;
+import org.apache.hadoop.gateway.descriptor.FilterParamDescriptor;
+import org.apache.hadoop.gateway.descriptor.ResourceDescriptor;
+import org.apache.hadoop.gateway.topology.Provider;
+import org.apache.hadoop.gateway.topology.Service;
+
+import java.util.List;
+
+public class AccessTokenFederationContributor extends ProviderDeploymentContributorBase {
+
+  private static final String FILTER_CLASSNAME = "org.apache.hadoop.gateway.provider.federation.jwt.filter.AccessTokenFederationFilter";
+
+  @Override
+  public String getRole() {
+    return "federation";
+  }
+
+  @Override
+  public String getName() {
+    return "AccessTokenProvider";
+  }
+
+  @Override
+  public void contributeProvider( DeploymentContext context, Provider provider ) {
+  }
+
+  @Override
+  public void contributeFilter( DeploymentContext context, Provider provider, Service service, ResourceDescriptor resource, List<FilterParamDescriptor> params ) {
+    resource.addFilter().name( getName() ).role( getRole() ).impl( FILTER_CLASSNAME ).params( params );
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/d205789a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AccessTokenFederationFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AccessTokenFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AccessTokenFederationFilter.java
new file mode 100644
index 0000000..179d2ee
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AccessTokenFederationFilter.java
@@ -0,0 +1,136 @@
+/**
+ * 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.provider.federation.jwt.filter;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.hadoop.gateway.provider.federation.jwt.AccessToken;
+import org.apache.hadoop.gateway.services.GatewayServices;
+import org.apache.hadoop.gateway.services.security.CryptoService;
+
+public class AccessTokenFederationFilter implements Filter {
+  private static final String BEARER = "Bearer ";
+  
+  private CryptoService crypto = null;
+  
+  @Override
+  public void init( FilterConfig filterConfig ) throws ServletException {
+    GatewayServices services = (GatewayServices) filterConfig.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
+    crypto = (CryptoService) services.getService(GatewayServices.CRYPTO_SERVICE);
+  }
+
+  public void destroy() {
+  }
+
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
+      throws IOException, ServletException {
+    String header = ((HttpServletRequest) request).getHeader("Authorization");
+    if (header != null && header.startsWith(BEARER)) {
+      // 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());
+      AccessToken token = AccessToken.parseToken(crypto, wireToken);
+// LJM TODO: replace with actual verification - should we do it in the authority? Probably.
+//      boolean verified = authority.verifyAccessToken(token);
+      boolean verified = true;
+      if (verified) {
+        // TODO: validate expiration
+        // TODO: confirm that audience matches intended target
+        // 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);
+      }
+      else {
+        ((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
+        return; //break filter chain
+      }
+    }
+    else {
+      // no token provided in header
+      // TODO: may have to check cookie and url as well before sending error
+      ((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
+      return; //break filter chain
+    }
+  }
+  
+  private void continueWithEstablishedSecurityContext(Subject subject, final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
+    try {
+      Subject.doAs(
+        subject,
+        new PrivilegedExceptionAction<Object>() {
+          @Override
+          public Object run() throws Exception {
+            chain.doFilter(request, response);
+            return null;
+          }
+        }
+        );
+    }
+    catch (PrivilegedActionException e) {
+      Throwable t = e.getCause();
+      if (t instanceof IOException) {
+        throw (IOException) t;
+      }
+      else if (t instanceof ServletException) {
+        throw (ServletException) t;
+      }
+      else {
+        throw new ServletException(t);
+      }
+    }
+  }
+  
+  private Subject createSubjectFromToken(AccessToken token) {
+    final String principal = token.getPrincipalName();
+
+    HashSet emptySet = new HashSet();
+    Set<Principal> principals = new HashSet<Principal>();
+    Principal p = new Principal() {
+      @Override
+      public String getName() {
+        return 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/incubator-knox/blob/d205789a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTAuthCodeAssertionFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTAuthCodeAssertionFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTAuthCodeAssertionFilter.java
index 7dfacb5..072c308 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTAuthCodeAssertionFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTAuthCodeAssertionFilter.java
@@ -18,18 +18,65 @@
 package org.apache.hadoop.gateway.provider.federation.jwt.filter;
 
 import java.io.IOException;
+import java.security.AccessController;
+import java.util.HashMap;
 
+import javax.security.auth.Subject;
 import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import org.apache.hadoop.gateway.filter.security.AbstractIdentityAssertionFilter;
+import org.apache.hadoop.gateway.provider.federation.jwt.JWTAuthority;
+import org.apache.hadoop.gateway.provider.federation.jwt.JWTToken;
+import org.apache.hadoop.gateway.services.GatewayServices;
+import org.apache.hadoop.gateway.services.security.CryptoService;
+import org.apache.hadoop.gateway.util.JsonUtils;
 
 public class JWTAuthCodeAssertionFilter extends AbstractIdentityAssertionFilter {
+  private static final String BEARER = "Bearer ";
+  
+  private CryptoService crypto = null;
 
   @Override
+  public void init( FilterConfig filterConfig ) throws ServletException {
+    super.init(filterConfig);
+    String validityStr = filterConfig.getInitParameter("validity");
+    if (validityStr == null) {
+      validityStr = "3600"; // 1 hr. in secs
+    }
+//    validity = Long.parseLong(validityStr);
+
+    GatewayServices services = (GatewayServices) filterConfig.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
+    crypto = (CryptoService) services.getService(GatewayServices.CRYPTO_SERVICE);
+  }
+  
+  @Override
   public void doFilter(ServletRequest request, ServletResponse response,
       FilterChain chain) throws IOException, ServletException {
+
+      Subject subject = Subject.getSubject(AccessController.getContext());
+      String principalName = getPrincipalName(subject);
+      principalName = mapper.mapPrincipal(principalName);
+      JWTAuthority authority = new JWTAuthority(crypto);
+      JWTToken authCode = authority.issueToken(subject, "RS256");
+      
+      HashMap<String, Object> map = new HashMap<String, Object>();
+      // TODO: populate map from JWT authorization code
+      map.put("iss", authCode.getIssuer());
+      map.put("sub", authCode.getPrincipal());
+      map.put("aud", authCode.getAudience());
+      map.put("exp", authCode.getExpires());
+      map.put("code", authCode.toString());
+      
+      String jsonResponse = JsonUtils.renderAsJsonString(map);
+      
+      response.getWriter().write(jsonResponse);
+      response.getWriter().flush();
+      return; // break filter chain
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/d205789a/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor b/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
index 5915713..2300504 100644
--- a/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
+++ b/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
@@ -17,4 +17,6 @@
 ##########################################################################
 
 org.apache.hadoop.gateway.provider.federation.jwt.deploy.JWTFederationContributor
-org.apache.hadoop.gateway.provider.federation.jwt.deploy.JWTAccessTokenAssertionContributor
\ No newline at end of file
+org.apache.hadoop.gateway.provider.federation.jwt.deploy.JWTAccessTokenAssertionContributor
+org.apache.hadoop.gateway.provider.federation.jwt.deploy.JWTAuthCodeAssertionContributor
+org.apache.hadoop.gateway.provider.federation.jwt.deploy.AccessTokenFederationContributor

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/d205789a/gateway-release/home/deployments/sample.xml
----------------------------------------------------------------------
diff --git a/gateway-release/home/deployments/sample.xml b/gateway-release/home/deployments/sample.xml
index 616c57e..d1d3b2b 100644
--- a/gateway-release/home/deployments/sample.xml
+++ b/gateway-release/home/deployments/sample.xml
@@ -21,6 +21,7 @@
         <provider>
             <role>authentication</role>
             <enabled>true</enabled>
+            <name>ShiroProvider</name>
             <param>
                 <name>main.ldapRealm</name>
                 <value>org.apache.shiro.realm.ldap.JndiLdapRealm</value>
@@ -42,6 +43,11 @@
                 <value>authcBasic</value>
             </param>
         </provider>
+        <provider>
+        	<role>identity-assertion</role>
+        	<enabled>true</enabled>
+        	<name>Pseudo</name>
+        </provider>
     </gateway>
 
     <service>

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/d205789a/gateway-release/home/templates/topology.xml
----------------------------------------------------------------------
diff --git a/gateway-release/home/templates/topology.xml b/gateway-release/home/templates/topology.xml
index 4c2dd69..1ef62a9 100644
--- a/gateway-release/home/templates/topology.xml
+++ b/gateway-release/home/templates/topology.xml
@@ -21,6 +21,7 @@
         <provider>
             <role>authentication</role>
             <enabled>true</enabled>
+            <name>ShiroProvider</name>
             <param>
                 <name>main.ldapRealm</name>
                 <value>org.apache.shiro.realm.ldap.JndiLdapRealm</value>
@@ -42,6 +43,11 @@
                 <value>authcBasic</value>
             </param>
         </provider>
+        <provider>
+        	<role>identity-assertion</role>
+        	<enabled>true</enabled>
+        	<name>Pseudo</name>
+        </provider>
     </gateway>
 
     <service>

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/d205789a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultCryptoService.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultCryptoService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultCryptoService.java
index c942271..7d1e1f9 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultCryptoService.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultCryptoService.java
@@ -37,6 +37,7 @@ import org.apache.hadoop.gateway.services.security.KeystoreServiceException;
 import org.apache.hadoop.gateway.services.ServiceLifecycleException;
 
 public class DefaultCryptoService implements CryptoService {
+  private static final String GATEWAY_IDENTITY_PASSPHRASE = "gateway-identity-passphrase";
   
   private AliasService as = null;
   private KeystoreService ks = null;
@@ -154,7 +155,8 @@ public class DefaultCryptoService implements CryptoService {
   @Override
   public byte[] sign(String algorithm, String alias, String payloadToSign) {
     try {
-      PrivateKey privateKey = (PrivateKey) ks.getKeyForGateway(alias);
+      char[] passphrase = as.getPasswordFromAliasForGateway(GATEWAY_IDENTITY_PASSPHRASE);
+      PrivateKey privateKey = (PrivateKey) ks.getKeyForGateway(alias, passphrase);
       Signature signature = Signature.getInstance(algorithm);
       signature.initSign(privateKey);
       signature.update(payloadToSign.getBytes("UTF-8"));

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/d205789a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java
index 88458ba..6670fb5 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java
@@ -259,12 +259,12 @@ public class DefaultKeystoreService implements KeystoreService {
   }
 
   @Override
-  public Key getKeyForGateway(String alias) throws KeystoreServiceException {
+  public Key getKeyForGateway(String alias, char[] passphrase) throws KeystoreServiceException {
     Key key = null;
     KeyStore ks = getKeystoreForGateway();
     if (ks != null) {
       try {
-        key = ks.getKey(alias, masterService.getMasterSecret());
+        key = ks.getKey(alias, passphrase);
       } catch (UnrecoverableKeyException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/d205789a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java
index 65c8a2a..848a522 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java
@@ -35,7 +35,7 @@ public interface KeystoreService extends Service {
   
   public KeyStore getKeystoreForGateway();
   
-  public Key getKeyForGateway(String alias) throws KeystoreServiceException;
+  public Key getKeyForGateway(String alias, char[] passphrase) throws KeystoreServiceException;
 
   public void createCredentialStoreForCluster(String clusterName);