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/03/15 16:37:00 UTC
git commit: Added support for JWT token based identity federation
Updated Branches:
refs/heads/master e09ea97d1 -> ba9dc3e5f
Added support for JWT token based identity federation
Project: http://git-wip-us.apache.org/repos/asf/incubator-knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-knox/commit/ba9dc3e5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-knox/tree/ba9dc3e5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-knox/diff/ba9dc3e5
Branch: refs/heads/master
Commit: ba9dc3e5f55a54753b15108e2a64bc5ab67cd15f
Parents: e09ea97
Author: Larry McCay <lm...@hortonworks.com>
Authored: Fri Mar 15 11:36:06 2013 -0400
Committer: Larry McCay <lm...@hortonworks.com>
Committed: Fri Mar 15 11:36:06 2013 -0400
----------------------------------------------------------------------
gateway-provider-security-jwt/pom.xml | 79 ++++++++
.../authn/jwt/deploy/JWTDeploymentContributor.java | 52 ++++++
.../provider/authn/jwt/filter/JWTAuthority.java | 60 +++++++
.../authn/jwt/filter/JWTFederationFilter.java | 135 ++++++++++++++
.../provider/authn/jwt/filter/JWTToken.java | 136 ++++++++++++++
.../jwt/deploy/JWTDeploymentContributor.java | 52 ++++++
.../federation/jwt/filter/JWTAuthority.java | 60 +++++++
.../federation/jwt/filter/JWTFederationFilter.java | 135 ++++++++++++++
.../federation/jwt/filter/JWTProviderMessages.java | 24 +++
.../provider/federation/jwt/filter/JWTToken.java | 138 +++++++++++++++
...op.gateway.deploy.ProviderDeploymentContributor | 19 ++
.../gateway/provider/federation/JWTTokenTest.java | 40 ++++
gateway-release/pom.xml | 4 +
.../gateway/services/DefaultGatewayServices.java | 1 +
.../security/impl/DefaultAliasService.java | 13 ++
.../security/impl/DefaultCryptoService.java | 68 +++++++
.../security/impl/DefaultKeystoreService.java | 22 +++
.../services/security/CryptoServiceTest.java | 12 ++
.../gateway/hdfs/HdfsDeploymentContributor.java | 7 +-
.../gateway/oozie/OozieDeploymentContributor.java | 7 +-
.../templeton/TempletonDeploymentContributor.java | 7 +-
.../deploy/ServiceDeploymentContributorBase.java | 14 ++
.../gateway/services/security/AliasService.java | 6 +
.../gateway/services/security/CryptoService.java | 4 +
.../gateway/services/security/KeystoreService.java | 3 +
pom.xml | 7 +-
26 files changed, 1101 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-provider-security-jwt/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/pom.xml b/gateway-provider-security-jwt/pom.xml
new file mode 100644
index 0000000..c436be9
--- /dev/null
+++ b/gateway-provider-security-jwt/pom.xml
@@ -0,0 +1,79 @@
+<!--
+ 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.hadoop</groupId>
+ <artifactId>gateway</artifactId>
+ <version>0.2.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>gateway-provider-security-jwt</artifactId>
+
+ <name>gateway-provider-security-jwt</name>
+ <description>An extension of the gateway introducing JWT as a recognized token for authentication.</description>
+
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+ <dependencies>
+ <dependency>
+ <groupId>${gateway-group}</groupId>
+ <artifactId>gateway-spi</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.jayway.jsonpath</groupId>
+ <artifactId>json-path</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>gateway-test-utils</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/deploy/JWTDeploymentContributor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/deploy/JWTDeploymentContributor.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/deploy/JWTDeploymentContributor.java
new file mode 100644
index 0000000..f842c36
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/deploy/JWTDeploymentContributor.java
@@ -0,0 +1,52 @@
+/**
+ * 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.authn.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 org.jboss.shrinkwrap.api.asset.StringAsset;
+
+import java.util.List;
+
+public class JWTDeploymentContributor extends ProviderDeploymentContributorBase {
+
+ private static final String FILTER_CLASSNAME = "org.apache.hadoop.gateway.provider.authn.jwt.filter.JWTFederationFilter";
+
+ @Override
+ public String getRole() {
+ return "federation";
+ }
+
+ @Override
+ public String getName() {
+ return "JWTProvider";
+ }
+
+ @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/ba9dc3e5/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTAuthority.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTAuthority.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTAuthority.java
new file mode 100644
index 0000000..9e47350
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTAuthority.java
@@ -0,0 +1,60 @@
+ /**
+ * 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.authn.jwt.filter;
+
+import java.security.Principal;
+
+import javax.security.auth.Subject;
+
+import org.apache.hadoop.gateway.services.security.CryptoService;
+
+public class JWTAuthority {
+ private CryptoService crypto = null;
+
+ public JWTAuthority(CryptoService crypto) {
+ this.crypto = crypto;
+ }
+
+ public JWTToken issueToken(Subject subject) {
+ Principal p = (Principal) subject.getPrincipals().toArray()[0];
+ String[] claimArray = new String[4];
+ claimArray[0] = "gateway";
+ claimArray[1] = p.getName();
+ // TODO: what do we need here and how do we determine what it should be?
+ claimArray[2] = "https://login.hadoop.example.org";
+ // TODO: make the validity period configurable
+ claimArray[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
+
+ JWTToken token = new JWTToken("RS256", claimArray);
+ signToken(token);
+
+ return token;
+ }
+
+ private void signToken(JWTToken token) {
+ byte[] signature = null;
+ signature = crypto.sign("SHA256withRSA","gateway-identity",token.getPayloadToSign());
+ token.setSignaturePayload(signature);
+ }
+
+ public boolean verifyToken(JWTToken token) {
+ boolean rc = false;
+ rc = crypto.verify("SHA256withRSA", "gateway-identity", token.getPayloadToSign(), token.getSignaturePayload());
+ return rc;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTFederationFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTFederationFilter.java
new file mode 100644
index 0000000..af76e09
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTFederationFilter.java
@@ -0,0 +1,135 @@
+ /**
+ * 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.authn.jwt.filter;
+
+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.services.GatewayServices;
+import org.apache.hadoop.gateway.services.security.CryptoService;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashSet;
+import java.util.Set;
+
+
+public class JWTFederationFilter implements Filter {
+
+ private static final String BEARER = "Bearer ";
+ private static final String GATEWAY_SERVICES_ATTRIBUTE = "org.apache.hadoop.gateway.gateway.services";
+
+ private JWTAuthority authority = null;
+
+ @Override
+ public void init( FilterConfig filterConfig ) throws ServletException {
+ GatewayServices services = (GatewayServices) filterConfig.getServletContext().getAttribute(GATEWAY_SERVICES_ATTRIBUTE);
+ CryptoService crypto = (CryptoService) services.getService("CryptoService");
+ authority = new JWTAuthority(crypto);
+ }
+
+ 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());
+ JWTToken token = JWTToken.parseToken(wireToken);
+ boolean verified = authority.verifyToken(token);
+ if (verified) {
+ // TODO: validate expiration
+ // TODO: confirm that audience matches intended target
+ 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(JWTToken token) {
+ final String principal = token.getPrincipal();
+
+ 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/ba9dc3e5/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTToken.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTToken.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTToken.java
new file mode 100644
index 0000000..d8544a3
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/authn/jwt/filter/JWTToken.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.authn.jwt.filter;
+
+import java.io.UnsupportedEncodingException;
+import java.text.MessageFormat;
+
+import org.apache.commons.codec.binary.Base64;
+
+import com.jayway.jsonpath.JsonPath;
+
+public class JWTToken {
+ private static final String headerTemplate = "'{'\"alg\": \"{0}\"'}'";
+ private static final String claimTemplate = "'{'\"iss\": \"{0}\", \"prn\": \"{1}\", \"aud\": \"{2}\", \"exp\": \"{3}\"'}'";
+ public static final String PRINCIPAL = "prn";
+ public static final String ISSUER = "iss";
+ public static final String AUDIENCE = "aud";
+ public static final String EXPIRES = "exp";
+
+ public String header = null;
+ public String claims = null;
+
+ byte[] payload = null;
+
+ public JWTToken(byte[] header, byte[] claims, byte[] signature) {
+ try {
+ this.header = new String(header, "UTF-8");
+ this.claims = new String(claims, "UTF-8");
+ this.payload = signature;
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public JWTToken(String alg, String[] claimsArray) {
+ MessageFormat headerFormatter = new MessageFormat(headerTemplate);
+ String[] algArray = new String[1];
+ algArray[0] = alg;
+ header = headerFormatter.format(algArray);
+
+ MessageFormat claimsFormatter = new MessageFormat(claimTemplate);
+ claims = claimsFormatter.format(claimsArray);
+ }
+
+ public String getPayloadToSign() {
+ StringBuffer sb = new StringBuffer();
+ try {
+ sb.append(Base64.encodeBase64URLSafeString(header.getBytes("UTF-8")));
+ sb.append(".");
+ sb.append(Base64.encodeBase64URLSafeString(claims.getBytes("UTF-8")));
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return sb.toString();
+ }
+
+ public String toString() {
+ System.out.println("Rendering JWT token for the wire");
+ StringBuffer sb = new StringBuffer();
+ try {
+ sb.append(Base64.encodeBase64URLSafeString(header.getBytes("UTF-8")));
+ sb.append(".");
+ sb.append(Base64.encodeBase64URLSafeString(claims.getBytes("UTF-8")));
+ sb.append(".");
+ sb.append(Base64.encodeBase64URLSafeString(payload));
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ System.out.println("Returning JWT token for the wire: " + sb.toString());
+ return sb.toString();
+ }
+
+ public void setSignaturePayload(byte[] payload) {
+ this.payload = payload;
+ }
+
+ public byte[] getSignaturePayload() {
+ return this.payload;
+ }
+
+ public static JWTToken parseToken(String wireToken) {
+ JWTToken token = null;
+ System.out.println("token off the wire: " + wireToken);
+ String[] parts = wireToken.split("\\.");
+ token = new JWTToken(Base64.decodeBase64(parts[0]), Base64.decodeBase64(parts[1]), Base64.decodeBase64(parts[2]));
+ System.out.println("header: " + token.header);
+ System.out.println("claims: " + token.claims);
+ System.out.println("payload: " + new String(token.payload));
+
+ return token;
+ }
+
+ public String getClaim(String claimName) {
+ String claim = null;
+
+ claim = JsonPath.read(claims, "$." + claimName);
+
+ return claim;
+ }
+
+ public String getPrincipal() {
+ return getClaim(JWTToken.PRINCIPAL);
+ }
+
+ public String getIssuer() {
+ return getClaim(JWTToken.ISSUER);
+ }
+
+ public String getAudience() {
+ return getClaim(JWTToken.AUDIENCE);
+ }
+
+ public String getExpires() {
+ return getClaim(JWTToken.EXPIRES);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/JWTDeploymentContributor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/JWTDeploymentContributor.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/JWTDeploymentContributor.java
new file mode 100644
index 0000000..32ac572
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/JWTDeploymentContributor.java
@@ -0,0 +1,52 @@
+/**
+ * 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 org.jboss.shrinkwrap.api.asset.StringAsset;
+
+import java.util.List;
+
+public class JWTDeploymentContributor extends ProviderDeploymentContributorBase {
+
+ private static final String FILTER_CLASSNAME = "org.apache.hadoop.gateway.provider.authn.jwt.filter.JWTFederationFilter";
+
+ @Override
+ public String getRole() {
+ return "federation";
+ }
+
+ @Override
+ public String getName() {
+ return "JWTProvider";
+ }
+
+ @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/ba9dc3e5/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTAuthority.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTAuthority.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTAuthority.java
new file mode 100644
index 0000000..8471ee0
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTAuthority.java
@@ -0,0 +1,60 @@
+ /**
+ * 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.security.Principal;
+
+import javax.security.auth.Subject;
+
+import org.apache.hadoop.gateway.services.security.CryptoService;
+
+public class JWTAuthority {
+ private CryptoService crypto = null;
+
+ public JWTAuthority(CryptoService crypto) {
+ this.crypto = crypto;
+ }
+
+ public JWTToken issueToken(Subject subject) {
+ Principal p = (Principal) subject.getPrincipals().toArray()[0];
+ String[] claimArray = new String[4];
+ claimArray[0] = "gateway";
+ claimArray[1] = p.getName();
+ // TODO: what do we need here and how do we determine what it should be?
+ claimArray[2] = "https://login.hadoop.example.org";
+ // TODO: make the validity period configurable
+ claimArray[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
+
+ JWTToken token = new JWTToken("RS256", claimArray);
+ signToken(token);
+
+ return token;
+ }
+
+ private void signToken(JWTToken token) {
+ byte[] signature = null;
+ signature = crypto.sign("SHA256withRSA","gateway-identity",token.getPayloadToSign());
+ token.setSignaturePayload(signature);
+ }
+
+ public boolean verifyToken(JWTToken token) {
+ boolean rc = false;
+ rc = crypto.verify("SHA256withRSA", "gateway-identity", token.getPayloadToSign(), token.getSignaturePayload());
+ return rc;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/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
new file mode 100644
index 0000000..be92b40
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
@@ -0,0 +1,135 @@
+ /**
+ * 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 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.services.GatewayServices;
+import org.apache.hadoop.gateway.services.security.CryptoService;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashSet;
+import java.util.Set;
+
+
+public class JWTFederationFilter implements Filter {
+
+ private static final String BEARER = "Bearer ";
+ private static final String GATEWAY_SERVICES_ATTRIBUTE = "org.apache.hadoop.gateway.gateway.services";
+
+ private JWTAuthority authority = null;
+
+ @Override
+ public void init( FilterConfig filterConfig ) throws ServletException {
+ GatewayServices services = (GatewayServices) filterConfig.getServletContext().getAttribute(GATEWAY_SERVICES_ATTRIBUTE);
+ CryptoService crypto = (CryptoService) services.getService("CryptoService");
+ authority = new JWTAuthority(crypto);
+ }
+
+ 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());
+ JWTToken token = JWTToken.parseToken(wireToken);
+ boolean verified = authority.verifyToken(token);
+ if (verified) {
+ // TODO: validate expiration
+ // TODO: confirm that audience matches intended target
+ 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(JWTToken token) {
+ final String principal = token.getPrincipal();
+
+ 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/ba9dc3e5/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTProviderMessages.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTProviderMessages.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTProviderMessages.java
new file mode 100644
index 0000000..af2eabe
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTProviderMessages.java
@@ -0,0 +1,24 @@
+package org.apache.hadoop.gateway.provider.federation.jwt.filter;
+
+import org.apache.commons.cli.ParseException;
+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;
+
+import java.io.File;
+import java.net.URI;
+
+/**
+ *
+ */
+@Messages(logger="org.apache.hadoop.gateway")
+public interface JWTProviderMessages {
+
+ @Message( level = MessageLevel.DEBUG, text = "Rendering JWT Token for the wire: {0}" )
+ void renderingJWTTokenForTheWire(String string);
+
+ @Message( level = MessageLevel.DEBUG, text = "Parsing JWT Token from the wire: {0}" )
+ void parsingToken(String wireToken);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTToken.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTToken.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTToken.java
new file mode 100644
index 0000000..4da185d
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTToken.java
@@ -0,0 +1,138 @@
+ /**
+ * 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.UnsupportedEncodingException;
+import java.text.MessageFormat;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
+
+import com.jayway.jsonpath.JsonPath;
+
+public class JWTToken {
+ private static final String headerTemplate = "'{'\"alg\": \"{0}\"'}'";
+ private static final String claimTemplate = "'{'\"iss\": \"{0}\", \"prn\": \"{1}\", \"aud\": \"{2}\", \"exp\": \"{3}\"'}'";
+ public static final String PRINCIPAL = "prn";
+ public static final String ISSUER = "iss";
+ public static final String AUDIENCE = "aud";
+ public static final String EXPIRES = "exp";
+ private static JWTProviderMessages log = MessagesFactory.get( JWTProviderMessages.class );
+
+ public String header = null;
+ public String claims = null;
+
+ byte[] payload = null;
+
+ public JWTToken(byte[] header, byte[] claims, byte[] signature) {
+ try {
+ this.header = new String(header, "UTF-8");
+ this.claims = new String(claims, "UTF-8");
+ this.payload = signature;
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public JWTToken(String alg, String[] claimsArray) {
+ MessageFormat headerFormatter = new MessageFormat(headerTemplate);
+ String[] algArray = new String[1];
+ algArray[0] = alg;
+ header = headerFormatter.format(algArray);
+
+ MessageFormat claimsFormatter = new MessageFormat(claimTemplate);
+ claims = claimsFormatter.format(claimsArray);
+ }
+
+ public String getPayloadToSign() {
+ StringBuffer sb = new StringBuffer();
+ try {
+ sb.append(Base64.encodeBase64URLSafeString(header.getBytes("UTF-8")));
+ sb.append(".");
+ sb.append(Base64.encodeBase64URLSafeString(claims.getBytes("UTF-8")));
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return sb.toString();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ try {
+ sb.append(Base64.encodeBase64URLSafeString(header.getBytes("UTF-8")));
+ sb.append(".");
+ sb.append(Base64.encodeBase64URLSafeString(claims.getBytes("UTF-8")));
+ sb.append(".");
+ sb.append(Base64.encodeBase64URLSafeString(payload));
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ log.renderingJWTTokenForTheWire(sb.toString());
+
+ return sb.toString();
+ }
+
+ public void setSignaturePayload(byte[] payload) {
+ this.payload = payload;
+ }
+
+ public byte[] getSignaturePayload() {
+ return this.payload;
+ }
+
+ public static JWTToken parseToken(String wireToken) {
+ JWTToken token = null;
+ log.parsingToken(wireToken);
+ String[] parts = wireToken.split("\\.");
+ token = new JWTToken(Base64.decodeBase64(parts[0]), Base64.decodeBase64(parts[1]), Base64.decodeBase64(parts[2]));
+ System.out.println("header: " + token.header);
+ System.out.println("claims: " + token.claims);
+ System.out.println("payload: " + new String(token.payload));
+
+ return token;
+ }
+
+ public String getClaim(String claimName) {
+ String claim = null;
+
+ claim = JsonPath.read(claims, "$." + claimName);
+
+ return claim;
+ }
+
+ public String getPrincipal() {
+ return getClaim(JWTToken.PRINCIPAL);
+ }
+
+ public String getIssuer() {
+ return getClaim(JWTToken.ISSUER);
+ }
+
+ public String getAudience() {
+ return getClaim(JWTToken.AUDIENCE);
+ }
+
+ public String getExpires() {
+ return getClaim(JWTToken.EXPIRES);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/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
new file mode 100644
index 0000000..58c6bf7
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
@@ -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.provider.federation.jwt.deploy.JWTDeploymentContributor
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/JWTTokenTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/JWTTokenTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/JWTTokenTest.java
new file mode 100644
index 0000000..bdc34a0
--- /dev/null
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/JWTTokenTest.java
@@ -0,0 +1,40 @@
+package org.apache.hadoop.gateway.provider.federation;
+
+import org.apache.hadoop.gateway.provider.federation.jwt.filter.JWTToken;
+import org.junit.Test;
+
+import junit.framework.TestCase;
+
+public class JWTTokenTest extends TestCase {
+
+ private static final String JWT_TOKEN = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiAiZ2F0ZXdheSIsICJwcm4iOiAiam9obi5kb2VAZXhhbXBsZS5jb20iLCAiYXVkIjogImh0dHBzOi8vbG9naW4uZXhhbXBsZS5jb20iLCAiZXhwIjogIjEzNjMzNjA5MTMifQ.AUecCHfxT84-zllHs6_XvQuIx8186Y9s5waNOILVBoV11b4RINvknVDhIyR-j35LUn2ayQ9J2e1psey3-slWCs9B40_W-VeG5mPdtT6Job9c6ZX_eIgwSh-d88MlYoSXNt2oWcabMi6HmKeOxc6MfX__R4AMKdgXx5Jido5RRiw";
+ private static final String HEADER = "{\"alg\":\"RS256\"}";
+ private static final String CLAIMS = "{\"iss\": \"gateway\", \"prn\": \"john.doe@example.com\", \"aud\": \"https://login.example.com\", \"exp\": \"1363360913\"}";
+
+ @Test
+ public void testTokenParsing() throws Exception {
+ JWTToken token = JWTToken.parseToken(JWT_TOKEN);
+
+ assertEquals(token.header, HEADER);
+ assertEquals(token.claims, CLAIMS);
+
+ assertEquals(token.getIssuer(), "gateway");
+ assertEquals(token.getPrincipal(), "john.doe@example.com");
+ assertEquals(token.getAudience(), "https://login.example.com");
+ assertEquals(token.getExpires(), "1363360913");
+ }
+
+ @Test
+ public void testTokenCreation() throws Exception {
+ String[] claims = new String[4];
+ claims[0] = "3MVG99OxTyEMCQ3gNp2PjkqeZKxnmAiG1xV4oHh9AKL_rSK.BoSVPGZHQukXnVjzRgSuQqGn75NL7yfkQcyy7";
+ claims[1] = "john.doe@example.com";
+ claims[2] = "https://login.example.com";
+ claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
+ JWTToken token = new JWTToken("RS256", claims);
+
+ assertEquals(token.getIssuer(), "3MVG99OxTyEMCQ3gNp2PjkqeZKxnmAiG1xV4oHh9AKL_rSK.BoSVPGZHQukXnVjzRgSuQqGn75NL7yfkQcyy7");
+ assertEquals(token.getPrincipal(), "john.doe@example.com");
+ assertEquals(token.getAudience(), "https://login.example.com");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-release/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-release/pom.xml b/gateway-release/pom.xml
index 0ec0a3e..a872983 100644
--- a/gateway-release/pom.xml
+++ b/gateway-release/pom.xml
@@ -117,6 +117,10 @@
</dependency>
<dependency>
<groupId>${gateway-group}</groupId>
+ <artifactId>gateway-provider-security-jwt</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${gateway-group}</groupId>
<artifactId>gateway-provider-identity-assertion-pseudo</artifactId>
</dependency>
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java
index fdd23d4..fcd3f66 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java
@@ -70,6 +70,7 @@ public class DefaultGatewayServices implements Service, ProviderDeploymentContri
services.put(ALIAS_SERVICE, alias);
DefaultCryptoService crypto = new DefaultCryptoService();
+ crypto.setKeystoreService(ks);
crypto.setAliasService(alias);
crypto.init(config, options);
services.put(CRYPTO_SERVICE, crypto);
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultAliasService.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultAliasService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultAliasService.java
index 57ef251..f16c07a 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultAliasService.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultAliasService.java
@@ -17,6 +17,8 @@
*/
package org.apache.hadoop.gateway.services.security.impl;
+import java.security.Key;
+import java.security.KeyStore;
import java.util.Map;
import java.util.Random;
@@ -24,6 +26,7 @@ import org.apache.hadoop.gateway.config.GatewayConfig;
import org.apache.hadoop.gateway.services.ServiceLifecycleException;
import org.apache.hadoop.gateway.services.security.AliasService;
import org.apache.hadoop.gateway.services.security.KeystoreService;
+import org.apache.hadoop.gateway.services.security.KeystoreServiceException;
public class DefaultAliasService implements AliasService {
@@ -102,4 +105,14 @@ public class DefaultAliasService implements AliasService {
keystoreService.addCredentialForCluster(clusterName, alias, value);
}
+ @Override
+ public char[] getPasswordFromAliasForGateway(String alias) {
+ return getPasswordFromAliasForCluster("__gateway", alias);
+ }
+
+ @Override
+ public void generateAliasForGateway(String alias) {
+ generateAliasForCluster("__gateway", alias);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/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 647cdc5..c942271 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
@@ -19,18 +19,31 @@ package org.apache.hadoop.gateway.services.security.impl;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
import java.util.Map;
+import org.apache.commons.codec.binary.Base64;
import org.apache.hadoop.gateway.config.GatewayConfig;
import org.apache.hadoop.gateway.services.security.AliasService;
import org.apache.hadoop.gateway.services.security.CryptoService;
import org.apache.hadoop.gateway.services.security.EncryptionResult;
+import org.apache.hadoop.gateway.services.security.KeystoreService;
+import org.apache.hadoop.gateway.services.security.KeystoreServiceException;
import org.apache.hadoop.gateway.services.ServiceLifecycleException;
public class DefaultCryptoService implements CryptoService {
private AliasService as = null;
+ private KeystoreService ks = null;
+
+ public void setKeystoreService(KeystoreService ks) {
+ this.ks = ks;
+ }
public CryptoService setAliasService(AliasService as) {
this.as = as;
@@ -109,4 +122,59 @@ public class DefaultCryptoService implements CryptoService {
}
return null;
}
+
+ @Override
+ public boolean verify(String algorithm, String alias, String signed, byte[] signature) {
+ boolean verified = false;
+ try {
+ Signature sig=Signature.getInstance(algorithm);
+ sig.initVerify(ks.getKeystoreForGateway().getCertificate(alias).getPublicKey());
+ sig.update(signed.getBytes("UTF-8"));
+ verified = sig.verify(signature);
+ } catch (SignatureException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvalidKeyException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ System.out.println("Signature verified: " + verified);
+ return verified;
+ }
+
+ @Override
+ public byte[] sign(String algorithm, String alias, String payloadToSign) {
+ try {
+ PrivateKey privateKey = (PrivateKey) ks.getKeyForGateway(alias);
+ Signature signature = Signature.getInstance(algorithm);
+ signature.initSign(privateKey);
+ signature.update(payloadToSign.getBytes("UTF-8"));
+ return signature.sign();
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvalidKeyException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (SignatureException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeystoreServiceException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return null;
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/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 c9545b2..88458ba 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
@@ -258,6 +258,27 @@ public class DefaultKeystoreService implements KeystoreService {
return false;
}
+ @Override
+ public Key getKeyForGateway(String alias) throws KeystoreServiceException {
+ Key key = null;
+ KeyStore ks = getKeystoreForGateway();
+ if (ks != null) {
+ try {
+ key = ks.getKey(alias, masterService.getMasterSecret());
+ } catch (UnrecoverableKeyException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ return key;
+ }
+
public KeyStore getCredentialStoreForCluster(String clusterName) {
final File keyStoreFile = new File( keyStoreDir + clusterName + CREDENTIALS_SUFFIX );
return getKeystore(keyStoreFile, "JCEKS");
@@ -308,6 +329,7 @@ public class DefaultKeystoreService implements KeystoreService {
public void writeKeystoreToFile(final KeyStore keyStore, final File file)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
+ // TODO: does this really need to be part of the interface?
// TODO: backup the keystore on disk before attempting a write and restore on failure
final FileOutputStream out = new FileOutputStream(file);
try
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-server/src/test/java/org/apache/hadoop/gateway/services/security/CryptoServiceTest.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/test/java/org/apache/hadoop/gateway/services/security/CryptoServiceTest.java b/gateway-server/src/test/java/org/apache/hadoop/gateway/services/security/CryptoServiceTest.java
index f42a291..d014242 100644
--- a/gateway-server/src/test/java/org/apache/hadoop/gateway/services/security/CryptoServiceTest.java
+++ b/gateway-server/src/test/java/org/apache/hadoop/gateway/services/security/CryptoServiceTest.java
@@ -73,6 +73,18 @@ public class CryptoServiceTest {
@Override
public void generateAliasForCluster(String clusterName, String alias) {
}
+
+ @Override
+ public char[] getPasswordFromAliasForGateway(String alias) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void generateAliasForGateway(String alias) {
+ // TODO Auto-generated method stub
+
+ }
};
cs = new DefaultCryptoService().setAliasService(as);
}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-service-hdfs/src/main/java/org/apache/hadoop/gateway/hdfs/HdfsDeploymentContributor.java
----------------------------------------------------------------------
diff --git a/gateway-service-hdfs/src/main/java/org/apache/hadoop/gateway/hdfs/HdfsDeploymentContributor.java b/gateway-service-hdfs/src/main/java/org/apache/hadoop/gateway/hdfs/HdfsDeploymentContributor.java
index 407a79b..721d8f0 100644
--- a/gateway-service-hdfs/src/main/java/org/apache/hadoop/gateway/hdfs/HdfsDeploymentContributor.java
+++ b/gateway-service-hdfs/src/main/java/org/apache/hadoop/gateway/hdfs/HdfsDeploymentContributor.java
@@ -119,7 +119,12 @@ public class HdfsDeploymentContributor extends ServiceDeploymentContributorBase
}
private void addAuthenticationFilter( DeploymentContext context, Service service, ResourceDescriptor resource ) {
- context.contributeFilter( service, resource, "authentication", null, null );
+ if (topologyContainsProviderType(context, "authentication")) {
+ context.contributeFilter( service, resource, "authentication", null, null );
+ }
+ if (topologyContainsProviderType(context, "federation")) {
+ context.contributeFilter( service, resource, "federation", null, null );
+ }
}
private void addIdentityAssertionFilter(DeploymentContext context, Service service, ResourceDescriptor resource) {
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-service-oozie/src/main/java/org/apache/hadoop/gateway/oozie/OozieDeploymentContributor.java
----------------------------------------------------------------------
diff --git a/gateway-service-oozie/src/main/java/org/apache/hadoop/gateway/oozie/OozieDeploymentContributor.java b/gateway-service-oozie/src/main/java/org/apache/hadoop/gateway/oozie/OozieDeploymentContributor.java
index 1b8f100..8ac1f31 100644
--- a/gateway-service-oozie/src/main/java/org/apache/hadoop/gateway/oozie/OozieDeploymentContributor.java
+++ b/gateway-service-oozie/src/main/java/org/apache/hadoop/gateway/oozie/OozieDeploymentContributor.java
@@ -84,7 +84,12 @@ public class OozieDeploymentContributor extends ServiceDeploymentContributorBase
}
private void addAuthenticationFilter( DeploymentContext context, Service service, ResourceDescriptor resource ) {
- context.contributeFilter( service, resource, "authentication", null, null );
+ if (topologyContainsProviderType(context, "authentication")) {
+ context.contributeFilter( service, resource, "authentication", null, null );
+ }
+ if (topologyContainsProviderType(context, "federation")) {
+ context.contributeFilter( service, resource, "federation", null, null );
+ }
}
private void addRewriteFilter(
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-service-templeton/src/main/java/org/apache/hadoop/gateway/templeton/TempletonDeploymentContributor.java
----------------------------------------------------------------------
diff --git a/gateway-service-templeton/src/main/java/org/apache/hadoop/gateway/templeton/TempletonDeploymentContributor.java b/gateway-service-templeton/src/main/java/org/apache/hadoop/gateway/templeton/TempletonDeploymentContributor.java
index 7600268..fcaf5cc 100644
--- a/gateway-service-templeton/src/main/java/org/apache/hadoop/gateway/templeton/TempletonDeploymentContributor.java
+++ b/gateway-service-templeton/src/main/java/org/apache/hadoop/gateway/templeton/TempletonDeploymentContributor.java
@@ -56,7 +56,12 @@ public class TempletonDeploymentContributor extends ServiceDeploymentContributor
ResourceDescriptor resource = context.getGatewayDescriptor().addResource();
resource.role( service.getRole() );
resource.pattern( TEMPLETON_EXTERNAL_PATH + "/**?**" );
- context.contributeFilter( service, resource, "authentication", null, null );
+ if (topologyContainsProviderType(context, "authentication")) {
+ context.contributeFilter( service, resource, "authentication", null, null );
+ }
+ if (topologyContainsProviderType(context, "federation")) {
+ context.contributeFilter( service, resource, "federation", null, null );
+ }
context.contributeFilter( service, resource, "rewrite", null, null );
context.contributeFilter( service, resource, "identity-assertion", null, null );
context.contributeFilter( service, resource, "dispatch", null, null );
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-spi/src/main/java/org/apache/hadoop/gateway/deploy/ServiceDeploymentContributorBase.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/deploy/ServiceDeploymentContributorBase.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/deploy/ServiceDeploymentContributorBase.java
index 7acca27..28d6db7 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/deploy/ServiceDeploymentContributorBase.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/deploy/ServiceDeploymentContributorBase.java
@@ -17,6 +17,10 @@
*/
package org.apache.hadoop.gateway.deploy;
+import java.util.Collection;
+
+import org.apache.hadoop.gateway.topology.Provider;
+
public abstract class ServiceDeploymentContributorBase extends DeploymentContributorBase implements ServiceDeploymentContributor {
public void initializeContribution( DeploymentContext context ) {
@@ -27,4 +31,14 @@ public abstract class ServiceDeploymentContributorBase extends DeploymentContrib
// Noop.
}
+ protected boolean topologyContainsProviderType(DeploymentContext context, String role) {
+ Collection<Provider> providers = context.getTopology().getProviders();
+ for (Provider provider : providers) {
+ if (role.equals(provider.getRole())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/AliasService.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/AliasService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/AliasService.java
index a2799bd..b32bbb1 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/AliasService.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/AliasService.java
@@ -17,6 +17,8 @@
*/
package org.apache.hadoop.gateway.services.security;
+import java.security.Key;
+
import org.apache.hadoop.gateway.services.Service;
public interface AliasService extends Service {
@@ -28,4 +30,8 @@ public interface AliasService extends Service {
public abstract char[] getPasswordFromAliasForCluster(String clusterName, String alias, boolean generate);
void generateAliasForCluster(String clusterName, String alias);
+
+ public abstract char[] getPasswordFromAliasForGateway(String alias);
+
+ void generateAliasForGateway(String alias);
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/CryptoService.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/CryptoService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/CryptoService.java
index 9b8527f..7d09f38 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/CryptoService.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/CryptoService.java
@@ -28,4 +28,8 @@ public interface CryptoService extends Service {
public byte[] decryptForCluster(String clusterName, String alias, String cipherText);
public byte[] decryptForCluster(String clusterName, String alias, byte[] cipherText, byte[] iv, byte[] salt);
+
+ public boolean verify(String algorithm, String alias, String payloadToSign, byte[] signaturePayload);
+
+ public byte[] sign(String algorithm, String alias, String payloadToSign);
}
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/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 3451207..65c8a2a 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
@@ -19,6 +19,7 @@ package org.apache.hadoop.gateway.services.security;
import java.io.File;
import java.io.IOException;
+import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@@ -34,6 +35,8 @@ public interface KeystoreService extends Service {
public KeyStore getKeystoreForGateway();
+ public Key getKeyForGateway(String alias) throws KeystoreServiceException;
+
public void createCredentialStoreForCluster(String clusterName);
public boolean isCredentialStoreForClusterAvailable(String clusterName) throws KeystoreServiceException;
http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/ba9dc3e5/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0ddf45d..93fa7c5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,6 +52,7 @@
<module>gateway-provider-rewrite</module>
<module>gateway-provider-hostmap-static</module>
<module>gateway-provider-secure-query</module>
+ <module>gateway-provider-security-jwt</module>
<module>gateway-provider-security-shiro</module>
<module>gateway-provider-identity-assertion-pseudo</module>
<module>gateway-service-hdfs</module>
@@ -272,6 +273,11 @@
</dependency>
<dependency>
<groupId>${gateway-group}</groupId>
+ <artifactId>gateway-provider-security-jwt</artifactId>
+ <version>${gateway-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${gateway-group}</groupId>
<artifactId>gateway-provider-security-shiro</artifactId>
<version>${gateway-version}</version>
</dependency>
@@ -617,7 +623,6 @@
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.8.1</version>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>