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 <lmc...@hortonworks.com> Authored: Thu Jan 26 17:34:09 2017 -0500 Committer: Larry McCay <lmc...@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>