Repository: knox Updated Branches: refs/heads/master 5f413f35e -> ca9247f4d
KNOX-1027 - Add support to configure the issuer for the JWT filters Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/ca9247f4 Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/ca9247f4 Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/ca9247f4 Branch: refs/heads/master Commit: ca9247f4d06eaeb8eb24b1696fd5a7b80cb13340 Parents: 5f413f3 Author: Colm O hEigeartaigh <cohei...@apache.org> Authored: Thu Sep 7 10:14:20 2017 +0100 Committer: Colm O hEigeartaigh <cohei...@apache.org> Committed: Fri Sep 8 09:58:27 2017 +0100 ---------------------------------------------------------------------- .../jwt/filter/AbstractJWTFilter.java | 26 ++++++-- .../jwt/filter/JWTFederationFilter.java | 8 ++- .../jwt/filter/SSOCookieFederationFilter.java | 6 +- .../federation/AbstractJWTFilterTest.java | 66 +++++++++++++++++++- 4 files changed, 95 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/ca9247f4/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java index e938480..d4c6717 100644 --- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java +++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java @@ -60,15 +60,24 @@ import org.apache.hadoop.gateway.services.security.token.impl.JWTToken; * */ public abstract class AbstractJWTFilter implements Filter { + /** + * If specified, this configuration property refers to a value which the issuer of a received + * token must match. Otherwise, the default value "KNOXSSO" is used + */ + public static final String JWT_EXPECTED_ISSUER = "jwt.expected.issuer"; + public static final String JWT_DEFAULT_ISSUER = "KNOXSSO"; + static JWTMessages log = MessagesFactory.get( JWTMessages.class ); - protected List<String> audiences; - protected JWTokenAuthority authority; - protected RSAPublicKey publicKey = null; private static AuditService auditService = AuditServiceFactory.getAuditService(); private static Auditor auditor = auditService.getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME ); + protected List<String> audiences; + protected JWTokenAuthority authority; + protected RSAPublicKey publicKey = null; + private String expectedIssuer; + public abstract void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; @@ -90,6 +99,13 @@ public abstract class AbstractJWTFilter implements Filter { } } + protected void configureExpectedIssuer(FilterConfig filterConfig) { + expectedIssuer = filterConfig.getInitParameter(JWT_EXPECTED_ISSUER);; + if (expectedIssuer == null) { + expectedIssuer = JWT_DEFAULT_ISSUER; + } + } + /** * @param expectedAudiences * @return @@ -222,8 +238,8 @@ public abstract class AbstractJWTFilter implements Filter { } if (verified) { - // confirm that issue matches intended target - which for this filter must be KNOXSSO - if (token.getIssuer().equals("KNOXSSO")) { + // confirm that issue matches intended target + if (expectedIssuer.equals(token.getIssuer())) { // if there is no expiration data then the lifecycle is tied entirely to // the cookie validity - otherwise ensure that the current time is before // the designated expiration time http://git-wip-us.apache.org/repos/asf/knox/blob/ca9247f4/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 2cbccf6..401e449 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 @@ -62,12 +62,14 @@ public class JWTFederationFilter extends AbstractJWTFilter { if (verificationPEM != null) { publicKey = CertificateUtils.parseRSAPublicKey(verificationPEM); } + + configureExpectedIssuer(filterConfig); } public void destroy() { } - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String header = ((HttpServletRequest) request).getHeader("Authorization"); String wireToken = null; @@ -79,7 +81,7 @@ public class JWTFederationFilter extends AbstractJWTFilter { // check for query param wireToken = ((HttpServletRequest) request).getParameter(paramName); } - + if (wireToken != null) { try { JWTToken token = new JWTToken(wireToken); @@ -100,7 +102,7 @@ public class JWTFederationFilter extends AbstractJWTFilter { protected void handleValidationError(HttpServletRequest request, HttpServletResponse response, int status, String error) throws IOException { if (error != null) { - response.sendError(status, error); + response.sendError(status, error); } else { response.sendError(status); http://git-wip-us.apache.org/repos/asf/knox/blob/ca9247f4/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java index 2e37c76..cf14863 100644 --- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java +++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java @@ -51,7 +51,7 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter { @Override public void init( FilterConfig filterConfig ) throws ServletException { super.init(filterConfig); - + // configured cookieName cookieName = filterConfig.getInitParameter(SSO_COOKIE_NAME); if (cookieName == null) { @@ -77,13 +77,15 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter { if (verificationPEM != null) { publicKey = CertificateUtils.parseRSAPublicKey(verificationPEM); } + + configureExpectedIssuer(filterConfig); } public void destroy() { } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String wireToken = null; HttpServletRequest req = (HttpServletRequest) request; http://git-wip-us.apache.org/repos/asf/knox/blob/ca9247f4/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java index 1893647..6f221a9 100644 --- a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java +++ b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java @@ -400,6 +400,65 @@ public abstract class AbstractJWTFilterTest { } } + @Test + public void testInvalidIssuer() throws Exception { + try { + Properties props = getProperties(); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("new-issuer", "alice", new Date(new Date().getTime() + 5000), privateKey); + + HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); + setTokenOnRequest(request, jwt); + + EasyMock.expect(request.getRequestURL()).andReturn( + new StringBuffer(SERVICE_URL)).anyTimes(); + EasyMock.expect(request.getQueryString()).andReturn(null); + HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); + EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn( + SERVICE_URL); + EasyMock.replay(request); + + TestFilterChain chain = new TestFilterChain(); + handler.doFilter(request, response, chain); + Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled); + Assert.assertTrue("No Subject should be returned.", chain.subject == null); + } catch (ServletException se) { + fail("Should NOT have thrown a ServletException."); + } + } + + @Test + public void testValidIssuerViaConfig() throws Exception { + try { + Properties props = getProperties(); + props.setProperty(AbstractJWTFilter.JWT_EXPECTED_ISSUER, "new-issuer"); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("new-issuer", "alice", new Date(new Date().getTime() + 5000), privateKey); + + HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); + setTokenOnRequest(request, jwt); + + EasyMock.expect(request.getRequestURL()).andReturn( + new StringBuffer(SERVICE_URL)).anyTimes(); + EasyMock.expect(request.getQueryString()).andReturn(null); + HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); + EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn( + SERVICE_URL); + EasyMock.replay(request); + + TestFilterChain chain = new TestFilterChain(); + handler.doFilter(request, response, chain); + Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled); + Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class); + Assert.assertTrue("No PrimaryPrincipal", principals.size() > 0); + Assert.assertEquals("Not the expected principal", "alice", ((Principal)principals.toArray()[0]).getName()); + } catch (ServletException se) { + fail("Should NOT have thrown a ServletException."); + } + } + protected Properties getProperties() { Properties props = new Properties(); props.setProperty( @@ -410,11 +469,16 @@ public abstract class AbstractJWTFilterTest { protected SignedJWT getJWT(String sub, Date expires, RSAPrivateKey privateKey, Properties props) throws Exception { + return getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, sub, expires, privateKey); + } + + protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey) + throws Exception { List<String> aud = new ArrayList<String>(); aud.add("bar"); JWTClaimsSet claims = new JWTClaimsSet.Builder() - .issuer("KNOXSSO") + .issuer(issuer) .subject(sub) .audience(aud) .expirationTime(expires)