http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/SSOCookieProviderTest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/SSOCookieProviderTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/SSOCookieProviderTest.java deleted file mode 100644 index 396aec9..0000000 --- a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/SSOCookieProviderTest.java +++ /dev/null @@ -1,205 +0,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. - */ -package org.apache.hadoop.gateway.provider.federation; - -import static org.junit.Assert.fail; - -import java.security.NoSuchAlgorithmException; -import java.security.Principal; -import java.util.Properties; -import java.util.Date; -import java.util.Set; - -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.hadoop.gateway.provider.federation.jwt.filter.SSOCookieFederationFilter; -import org.apache.hadoop.gateway.security.PrimaryPrincipal; -import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority; -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.nimbusds.jwt.SignedJWT; - -public class SSOCookieProviderTest extends AbstractJWTFilterTest { - private static final String SERVICE_URL = "https://localhost:8888/resource"; - private static final String REDIRECT_LOCATION = - "https://localhost:8443/authserver?originalUrl=" + SERVICE_URL; - - @Before - public void setup() throws Exception, NoSuchAlgorithmException { - super.setup(); - handler = new TestSSOCookieFederationProvider(); - ((TestSSOCookieFederationProvider) handler).setTokenService(new TestJWTokenAuthority()); - } - - protected void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt) { - Cookie cookie = new Cookie("hadoop-jwt", jwt.serialize()); - EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie }); - } - - protected void setGarbledTokenOnRequest(HttpServletRequest request, SignedJWT jwt) { - Cookie cookie = new Cookie("hadoop-jwt", "ljm" + jwt.serialize()); - EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie }); - } - - protected String getAudienceProperty() { - return TestSSOCookieFederationProvider.SSO_EXPECTED_AUDIENCES; - } - - @Test - public void testCustomCookieNameJWT() throws Exception { - try { - Properties props = getProperties(); - props.put("sso.cookie.name", "jowt"); - handler.init(new TestFilterConfig(props)); - - SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), - privateKey, props); - - Cookie cookie = new Cookie("jowt", jwt.serialize()); - HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); - EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie }); - 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 == true); - Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class); - Assert.assertTrue("No PrimaryPrincipal returned.", 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."); - } - } - - @Test - public void testNoProviderURLJWT() throws Exception { - try { - Properties props = getProperties(); - props.remove("sso.authentication.provider.url"); - handler.init(new TestFilterConfig(props)); - - fail("Servlet exception should have been thrown."); - - } catch (ServletException se) { - // expected - let's ensure it mentions the missing authentication provider URL - se.getMessage().contains("authentication provider URL is missing"); - } - } - -/* - @Test - public void testFailedSignatureValidationJWT() throws Exception { - try { - - // Create a public key that doesn't match the one needed to - // verify the signature - in order to make it fail verification... - KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); - kpg.initialize(2048); - - KeyPair kp = kpg.genKeyPair(); - RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic(); - - handler.setPublicKey(publicKey); - - Properties props = getProperties(); - handler.init(props); - - SignedJWT jwt = getJWT("bob", new Date(new Date().getTime() + 5000), - privateKey); - - Cookie cookie = new Cookie("hadoop-jwt", jwt.serialize()); - HttpServletRequest request = Mockito.mock(HttpServletRequest.class); - Mockito.when(request.getCookies()).thenReturn(new Cookie[] { cookie }); - Mockito.when(request.getRequestURL()).thenReturn( - new StringBuffer(SERVICE_URL)).anyTimes(); - HttpServletResponse response = Mockito.mock(HttpServletResponse.class); - Mockito.when(response.encodeRedirectURL(SERVICE_URL)).thenReturn( - SERVICE_URL); - - AuthenticationToken token = handler.alternateAuthenticate(request, - response); - Mockito.verify(response).sendRedirect(REDIRECT_LOCATION); - } catch (ServletException se) { - fail("alternateAuthentication should NOT have thrown a ServletException"); - } catch (AuthenticationException ae) { - fail("alternateAuthentication should NOT have thrown a AuthenticationException"); - } - } -*/ - - @Test - public void testOrigURLWithQueryString() throws Exception { - Properties props = getProperties(); - handler.init(new TestFilterConfig(props)); - - HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); - EasyMock.expect(request.getRequestURL()).andReturn( - new StringBuffer(SERVICE_URL)).anyTimes(); - EasyMock.expect(request.getQueryString()).andReturn("name=value"); - EasyMock.replay(request); - - String loginURL = ((TestSSOCookieFederationProvider)handler).testConstructLoginURL(request); - Assert.assertNotNull("loginURL should not be null.", loginURL); - Assert.assertEquals("https://localhost:8443/authserver?originalUrl=" + SERVICE_URL + "?name=value", loginURL); - } - - @Test - public void testOrigURLNoQueryString() throws Exception { - Properties props = getProperties(); - handler.init(new TestFilterConfig(props)); - - HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); - EasyMock.expect(request.getRequestURL()).andReturn( - new StringBuffer(SERVICE_URL)).anyTimes(); - EasyMock.expect(request.getQueryString()).andReturn(null); - EasyMock.replay(request); - - String loginURL = ((TestSSOCookieFederationProvider)handler).testConstructLoginURL(request); - Assert.assertNotNull("LoginURL should not be null.", loginURL); - Assert.assertEquals("https://localhost:8443/authserver?originalUrl=" + SERVICE_URL, loginURL); - } - - - @Override - protected String getVerificationPemProperty() { - return SSOCookieFederationFilter.SSO_VERIFICATION_PEM; - }; - - private static class TestSSOCookieFederationProvider extends SSOCookieFederationFilter { - public String testConstructLoginURL(HttpServletRequest req) { - return constructLoginURL(req); - } - - public void setTokenService(JWTokenAuthority ts) { - authority = ts; - } - }; - -}
http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java new file mode 100644 index 0000000..503f323 --- /dev/null +++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java @@ -0,0 +1,490 @@ +/** + * 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.knox.gateway.provider.federation; + +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.net.InetAddress; +import java.security.AccessController; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.cert.Certificate; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.text.MessageFormat; +import java.util.Enumeration; +import java.util.List; +import java.util.ArrayList; +import java.util.Properties; +import java.util.Date; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +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.commons.codec.binary.Base64; +import org.apache.knox.gateway.provider.federation.jwt.filter.AbstractJWTFilter; +import org.apache.knox.gateway.provider.federation.jwt.filter.SSOCookieFederationFilter; +import org.apache.knox.gateway.security.PrimaryPrincipal; +import org.apache.knox.gateway.services.security.impl.X509CertificateUtil; +import org.apache.knox.gateway.services.security.token.JWTokenAuthority; +import org.apache.knox.gateway.services.security.token.TokenServiceException; +import org.apache.knox.gateway.services.security.token.impl.JWT; +import org.apache.knox.gateway.services.security.token.impl.JWTToken; +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.nimbusds.jose.*; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.util.Base64URL; + +public abstract class AbstractJWTFilterTest { + private static final String SERVICE_URL = "https://localhost:8888/resource"; + private static final String dnTemplate = "CN={0},OU=Test,O=Hadoop,L=Test,ST=Test,C=US"; + + protected AbstractJWTFilter handler = null; + protected RSAPublicKey publicKey = null; + protected RSAPrivateKey privateKey = null; + protected String pem = null; + + protected abstract void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt); + protected abstract void setGarbledTokenOnRequest(HttpServletRequest request, SignedJWT jwt); + protected abstract String getAudienceProperty(); + protected abstract String getVerificationPemProperty(); + + private String buildDistinguishedName(String hostname) { + MessageFormat headerFormatter = new MessageFormat(dnTemplate); + String[] paramArray = new String[1]; + paramArray[0] = hostname; + String dn = headerFormatter.format(paramArray); + return dn; + } + + @Before + public void setup() throws Exception, NoSuchAlgorithmException { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + KeyPair KPair = kpg.generateKeyPair(); + String dn = buildDistinguishedName(InetAddress.getLocalHost().getHostName()); + Certificate cert = X509CertificateUtil.generateCertificate(dn, KPair, 365, "SHA1withRSA"); + byte[] data = cert.getEncoded(); + Base64 encoder = new Base64( 76, "\n".getBytes( "ASCII" ) ); + pem = new String(encoder.encodeToString( data ).getBytes( "ASCII" )).trim(); + + publicKey = (RSAPublicKey) KPair.getPublic(); + privateKey = (RSAPrivateKey) KPair.getPrivate(); + } + + @After + public void teardown() throws Exception { + handler.destroy(); + } + + @Test + public void testValidJWT() throws Exception { + try { + Properties props = getProperties(); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props); + + 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 == true); + 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."); + } + } + + @Test + public void testValidAudienceJWT() throws Exception { + try { + Properties props = getProperties(); + props.put(getAudienceProperty(), "bar"); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props); + + 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 == true); + 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."); + } + } + + @Test + public void testInvalidAudienceJWT() throws Exception { + try { + Properties props = getProperties(); + props.put(getAudienceProperty(), "foo"); + props.put("sso.authentication.provider.url", "https://localhost:8443/gateway/knoxsso/api/v1/websso"); + + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props); + + 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 == false); + Assert.assertTrue("No Subject should be returned.", chain.subject == null); + } catch (ServletException se) { + fail("Should NOT have thrown a ServletException."); + } + } + + @Test + public void testValidVerificationPEM() throws Exception { + try { + Properties props = getProperties(); + +// System.out.println("+" + pem + "+"); + + props.put(getAudienceProperty(), "bar"); + props.put("sso.authentication.provider.url", "https://localhost:8443/gateway/knoxsso/api/v1/websso"); + props.put(getVerificationPemProperty(), pem); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 50000), privateKey, props); + + 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 == true); + 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."); + } + } + + @Test + public void testExpiredJWT() throws Exception { + try { + Properties props = getProperties(); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() - 1000), privateKey, props); + + 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 == false); + Assert.assertTrue("No Subject should be returned.", chain.subject == null); + } catch (ServletException se) { + fail("Should NOT have thrown a ServletException."); + } + } + + @Test + public void testValidJWTNoExpiration() throws Exception { + try { + Properties props = getProperties(); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("alice", null, privateKey, props); + + 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).anyTimes(); + EasyMock.replay(request); + + TestFilterChain chain = new TestFilterChain(); + handler.doFilter(request, response, chain); + Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled == true); + 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."); + } + } + + @Test + public void testUnableToParseJWT() throws Exception { + try { + Properties props = getProperties(); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("bob",new Date(new Date().getTime() + 5000), privateKey, props); + + HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); + setGarbledTokenOnRequest(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).anyTimes(); + EasyMock.replay(request); + + TestFilterChain chain = new TestFilterChain(); + handler.doFilter(request, response, chain); + Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled == false); + Assert.assertTrue("No Subject should be returned.", chain.subject == null); + } catch (ServletException se) { + fail("Should NOT have thrown a ServletException."); + } + } + + protected Properties getProperties() { + Properties props = new Properties(); + props.setProperty( + SSOCookieFederationFilter.SSO_AUTHENTICATION_PROVIDER_URL, + "https://localhost:8443/authserver"); + return props; + } + + protected SignedJWT getJWT(String sub, Date expires, RSAPrivateKey privateKey, + Properties props) throws Exception { + List<String> aud = new ArrayList<String>(); + aud.add("bar"); + + JWTClaimsSet claims = new JWTClaimsSet.Builder() + .issuer("KNOXSSO") + .subject(sub) + .audience(aud) + .expirationTime(expires) + .claim("scope", "openid") + .build(); + + JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).build(); + + SignedJWT signedJWT = new SignedJWT(header, claims); + Base64URL sigInput = Base64URL.encode(signedJWT.getSigningInput()); + JWSSigner signer = new RSASSASigner(privateKey); + + signedJWT.sign(signer); + + return signedJWT; + } + + protected static class TestFilterConfig implements FilterConfig { + Properties props = null; + + public TestFilterConfig(Properties props) { + this.props = props; + } + + @Override + public String getFilterName() { + return null; + } + + /* (non-Javadoc) + * @see javax.servlet.FilterConfig#getServletContext() + */ + @Override + public ServletContext getServletContext() { +// JWTokenAuthority authority = EasyMock.createNiceMock(JWTokenAuthority.class); +// GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); +// EasyMock.expect(services.getService("TokenService").andReturn(authority)); +// ServletContext context = EasyMock.createNiceMock(ServletContext.class); +// EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE).andReturn(new DefaultGatewayServices())); + return null; + } + + /* (non-Javadoc) + * @see javax.servlet.FilterConfig#getInitParameter(java.lang.String) + */ + @Override + public String getInitParameter(String name) { + return props.getProperty(name, null); + } + + /* (non-Javadoc) + * @see javax.servlet.FilterConfig#getInitParameterNames() + */ + @Override + public Enumeration<String> getInitParameterNames() { + return null; + } + + } + + protected static class TestJWTokenAuthority implements JWTokenAuthority { + + /* (non-Javadoc) + * @see JWTokenAuthority#issueToken(javax.security.auth.Subject, java.lang.String) + */ + @Override + public JWTToken issueToken(Subject subject, String algorithm) + throws TokenServiceException { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String) + */ + @Override + public JWTToken issueToken(Principal p, String algorithm) + throws TokenServiceException { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, java.lang.String) + */ + @Override + public JWTToken issueToken(Principal p, String audience, String algorithm) + throws TokenServiceException { + return null; + } + + /* (non-Javadoc) + * @see JWTokenAuthority#verifyToken(JWTToken) + */ + @Override + public boolean verifyToken(JWTToken token) throws TokenServiceException { + return true; + } + + /* (non-Javadoc) + * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, java.lang.String, long) + */ + @Override + public JWTToken issueToken(Principal p, String audience, String algorithm, + long expires) throws TokenServiceException { + return null; + } + + @Override + public JWTToken issueToken(Principal p, List<String> audiences, String algorithm, + long expires) throws TokenServiceException { + return null; + } + + /* (non-Javadoc) + * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, long) + */ + @Override + public JWT issueToken(Principal p, String audience, long l) + throws TokenServiceException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean verifyToken(JWTToken token, RSAPublicKey publicKey) throws TokenServiceException { + // TODO Auto-generated method stub + return true; + } + + } + + protected static class TestFilterChain implements FilterChain { + boolean doFilterCalled = false; + Subject subject = null; + + /* (non-Javadoc) + * @see javax.servlet.FilterChain#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) + */ + @Override + public void doFilter(ServletRequest request, ServletResponse response) + throws IOException, ServletException { + doFilterCalled = true; + + subject = Subject.getSubject( AccessController.getContext() ); + } + + } +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java new file mode 100644 index 0000000..14b704a --- /dev/null +++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTFederationFilterTest.java @@ -0,0 +1,67 @@ +/** + * 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.knox.gateway.provider.federation; + +import java.security.NoSuchAlgorithmException; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.knox.gateway.provider.federation.jwt.filter.JWTFederationFilter; +import org.apache.knox.gateway.services.security.token.JWTokenAuthority; +import org.easymock.EasyMock; +import org.junit.Before; + +import com.nimbusds.jwt.SignedJWT; + +public class JWTFederationFilterTest extends AbstractJWTFilterTest { + + @Before + public void setup() throws Exception, NoSuchAlgorithmException { + super.setup(); + handler = new TestJWTFederationFilter(); + ((TestJWTFederationFilter) handler).setTokenService(new TestJWTokenAuthority()); + } + + protected void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt) { + String token = "Bearer " + jwt.serialize(); + EasyMock.expect(request.getHeader("Authorization")).andReturn(token); + } + + protected void setGarbledTokenOnRequest(HttpServletRequest request, SignedJWT jwt) { + String token = "Bearer " + "ljm" + jwt.serialize(); + EasyMock.expect(request.getHeader("Authorization")).andReturn(token); + } + + protected String getAudienceProperty() { + return TestJWTFederationFilter.KNOX_TOKEN_AUDIENCES; + } + + private static class TestJWTFederationFilter extends JWTFederationFilter { + + public void setTokenService(JWTokenAuthority ts) { + authority = ts; + } + + } + + @Override + protected String getVerificationPemProperty() { + return TestJWTFederationFilter.TOKEN_VERIFICATION_PEM; + }; + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTTokenTest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTTokenTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTTokenTest.java new file mode 100644 index 0000000..1f1973e --- /dev/null +++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/JWTTokenTest.java @@ -0,0 +1,133 @@ +/** + * 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.knox.gateway.provider.federation; + +import java.util.ArrayList; +import junit.framework.TestCase; + +import org.apache.knox.gateway.services.security.token.impl.JWTToken; +import org.junit.Test; + +public class JWTTokenTest extends TestCase { + private static final String JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MTY5MjkxMDksImp0aSI6ImFhN2Y4ZDBhOTVjIiwic2NvcGVzIjpbInJlcG8iLCJwdWJsaWNfcmVwbyJdfQ.XCEwpBGvOLma4TCoh36FU7XhUbcskygS81HE1uHLf0E"; + private static final String HEADER = "{\"alg\":\"RS256\", \"type\":\"JWT\"}"; + private static final String CLAIMS = "{\"iss\": \"gateway\", \"prn\": \"john....@example.com\", \"aud\": \"https://login.example.com\", \"exp\": \"1363360913\"}"; + +// public void testTokenParsing() throws Exception { +// try { +// JWTToken token = JWTToken.parseToken(JWT_TOKEN); +// assertEquals(token.getHeader(), HEADER); +// assertEquals(token.getClaims(), CLAIMS); +// +// assertEquals(token.getIssuer(), "gateway"); +// assertEquals(token.getPrincipal(), "john....@example.com"); +// assertEquals(token.getAudience(), "https://login.example.com"); +// assertEquals(token.getExpires(), "1363360913"); +// } +// catch (ParseException pe) { +// fail("ParseException encountered."); +// } +// } + + @Test + public void testTokenCreation() throws Exception { + String[] claims = new String[4]; + claims[0] = "KNOXSSO"; + claims[1] = "john....@example.com"; + claims[2] = "https://login.example.com"; + claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300); + JWTToken token = new JWTToken("RS256", claims); + + assertEquals("KNOXSSO", token.getIssuer()); + assertEquals("john....@example.com", token.getSubject()); + assertEquals("https://login.example.com", token.getAudience()); + } + + @Test + public void testTokenCreationWithAudienceListSingle() throws Exception { + String[] claims = new String[4]; + claims[0] = "KNOXSSO"; + claims[1] = "john....@example.com"; + claims[2] = null; + claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300); + ArrayList<String> audiences = new ArrayList<String>(); + audiences.add("https://login.example.com"); + + JWTToken token = new JWTToken("RS256", claims, audiences); + + assertEquals("KNOXSSO", token.getIssuer()); + assertEquals("john....@example.com", token.getSubject()); + assertEquals("https://login.example.com", token.getAudience()); + assertEquals(1, token.getAudienceClaims().length); + } + + @Test + public void testTokenCreationWithAudienceListMultiple() throws Exception { + String[] claims = new String[4]; + claims[0] = "KNOXSSO"; + claims[1] = "john....@example.com"; + claims[2] = null; + claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300); + ArrayList<String> audiences = new ArrayList<String>(); + audiences.add("https://login.example.com"); + audiences.add("KNOXSSO"); + + JWTToken token = new JWTToken("RS256", claims, audiences); + + assertEquals("KNOXSSO", token.getIssuer()); + assertEquals("john....@example.com", token.getSubject()); + assertEquals("https://login.example.com", token.getAudience()); + assertEquals(2, token.getAudienceClaims().length); + } + + @Test + public void testTokenCreationWithAudienceListCombined() throws Exception { + String[] claims = new String[4]; + claims[0] = "KNOXSSO"; + claims[1] = "john....@example.com"; + claims[2] = "LJM"; + claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300); + ArrayList<String> audiences = new ArrayList<String>(); + audiences.add("https://login.example.com"); + audiences.add("KNOXSSO"); + + JWTToken token = new JWTToken("RS256", claims, audiences); + + assertEquals("KNOXSSO", token.getIssuer()); + assertEquals("john....@example.com", token.getSubject()); + assertEquals("https://login.example.com", token.getAudience()); + assertEquals(3, token.getAudienceClaims().length); + } + + @Test + public void testTokenCreationWithNullAudienceList() throws Exception { + String[] claims = new String[4]; + claims[0] = "KNOXSSO"; + claims[1] = "john....@example.com"; + claims[2] = null; + claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300); + ArrayList<String> audiences = null; + + JWTToken token = new JWTToken("RS256", claims, audiences); + + assertEquals("KNOXSSO", token.getIssuer()); + assertEquals("john....@example.com", token.getSubject()); + assertEquals(null, token.getAudience()); + assertEquals(null, token.getAudienceClaims()); + } +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java new file mode 100644 index 0000000..fa0e785 --- /dev/null +++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java @@ -0,0 +1,205 @@ +/** + * 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.knox.gateway.provider.federation; + +import static org.junit.Assert.fail; + +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.util.Properties; +import java.util.Date; +import java.util.Set; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.knox.gateway.provider.federation.jwt.filter.SSOCookieFederationFilter; +import org.apache.knox.gateway.security.PrimaryPrincipal; +import org.apache.knox.gateway.services.security.token.JWTokenAuthority; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.nimbusds.jwt.SignedJWT; + +public class SSOCookieProviderTest extends AbstractJWTFilterTest { + private static final String SERVICE_URL = "https://localhost:8888/resource"; + private static final String REDIRECT_LOCATION = + "https://localhost:8443/authserver?originalUrl=" + SERVICE_URL; + + @Before + public void setup() throws Exception, NoSuchAlgorithmException { + super.setup(); + handler = new TestSSOCookieFederationProvider(); + ((TestSSOCookieFederationProvider) handler).setTokenService(new TestJWTokenAuthority()); + } + + protected void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt) { + Cookie cookie = new Cookie("hadoop-jwt", jwt.serialize()); + EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie }); + } + + protected void setGarbledTokenOnRequest(HttpServletRequest request, SignedJWT jwt) { + Cookie cookie = new Cookie("hadoop-jwt", "ljm" + jwt.serialize()); + EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie }); + } + + protected String getAudienceProperty() { + return TestSSOCookieFederationProvider.SSO_EXPECTED_AUDIENCES; + } + + @Test + public void testCustomCookieNameJWT() throws Exception { + try { + Properties props = getProperties(); + props.put("sso.cookie.name", "jowt"); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), + privateKey, props); + + Cookie cookie = new Cookie("jowt", jwt.serialize()); + HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); + EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie }); + 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 == true); + Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class); + Assert.assertTrue("No PrimaryPrincipal returned.", 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."); + } + } + + @Test + public void testNoProviderURLJWT() throws Exception { + try { + Properties props = getProperties(); + props.remove("sso.authentication.provider.url"); + handler.init(new TestFilterConfig(props)); + + fail("Servlet exception should have been thrown."); + + } catch (ServletException se) { + // expected - let's ensure it mentions the missing authentication provider URL + se.getMessage().contains("authentication provider URL is missing"); + } + } + +/* + @Test + public void testFailedSignatureValidationJWT() throws Exception { + try { + + // Create a public key that doesn't match the one needed to + // verify the signature - in order to make it fail verification... + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + + KeyPair kp = kpg.genKeyPair(); + RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic(); + + handler.setPublicKey(publicKey); + + Properties props = getProperties(); + handler.init(props); + + SignedJWT jwt = getJWT("bob", new Date(new Date().getTime() + 5000), + privateKey); + + Cookie cookie = new Cookie("hadoop-jwt", jwt.serialize()); + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + Mockito.when(request.getCookies()).thenReturn(new Cookie[] { cookie }); + Mockito.when(request.getRequestURL()).thenReturn( + new StringBuffer(SERVICE_URL)).anyTimes(); + HttpServletResponse response = Mockito.mock(HttpServletResponse.class); + Mockito.when(response.encodeRedirectURL(SERVICE_URL)).thenReturn( + SERVICE_URL); + + AuthenticationToken token = handler.alternateAuthenticate(request, + response); + Mockito.verify(response).sendRedirect(REDIRECT_LOCATION); + } catch (ServletException se) { + fail("alternateAuthentication should NOT have thrown a ServletException"); + } catch (AuthenticationException ae) { + fail("alternateAuthentication should NOT have thrown a AuthenticationException"); + } + } +*/ + + @Test + public void testOrigURLWithQueryString() throws Exception { + Properties props = getProperties(); + handler.init(new TestFilterConfig(props)); + + HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); + EasyMock.expect(request.getRequestURL()).andReturn( + new StringBuffer(SERVICE_URL)).anyTimes(); + EasyMock.expect(request.getQueryString()).andReturn("name=value"); + EasyMock.replay(request); + + String loginURL = ((TestSSOCookieFederationProvider)handler).testConstructLoginURL(request); + Assert.assertNotNull("loginURL should not be null.", loginURL); + Assert.assertEquals("https://localhost:8443/authserver?originalUrl=" + SERVICE_URL + "?name=value", loginURL); + } + + @Test + public void testOrigURLNoQueryString() throws Exception { + Properties props = getProperties(); + handler.init(new TestFilterConfig(props)); + + HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); + EasyMock.expect(request.getRequestURL()).andReturn( + new StringBuffer(SERVICE_URL)).anyTimes(); + EasyMock.expect(request.getQueryString()).andReturn(null); + EasyMock.replay(request); + + String loginURL = ((TestSSOCookieFederationProvider)handler).testConstructLoginURL(request); + Assert.assertNotNull("LoginURL should not be null.", loginURL); + Assert.assertEquals("https://localhost:8443/authserver?originalUrl=" + SERVICE_URL, loginURL); + } + + + @Override + protected String getVerificationPemProperty() { + return SSOCookieFederationFilter.SSO_VERIFICATION_PEM; + }; + + private static class TestSSOCookieFederationProvider extends SSOCookieFederationFilter { + public String testConstructLoginURL(HttpServletRequest req) { + return constructLoginURL(req); + } + + public void setTokenService(JWTokenAuthority ts) { + authority = ts; + } + }; + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/Pac4jMessages.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/Pac4jMessages.java b/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/Pac4jMessages.java deleted file mode 100644 index 931e494..0000000 --- a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/Pac4jMessages.java +++ /dev/null @@ -1,50 +0,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. - */ -package org.apache.hadoop.gateway.pac4j; - -import org.apache.hadoop.gateway.i18n.messages.Message; -import org.apache.hadoop.gateway.i18n.messages.MessageLevel; -import org.apache.hadoop.gateway.i18n.messages.Messages; - -/** - * Logging messages for the pac4j provider. - * - * @since 0.8.0 - */ -@Messages(logger="org.apache.hadoop.gateway.pac4j") -public interface Pac4jMessages { - - @Message( level = MessageLevel.ERROR, text = "pac4j callback URL required") - public void ssoAuthenticationProviderUrlRequired(); - - @Message( level = MessageLevel.ERROR, text = "pac4j clientName parameter required") - public void clientNameParameterRequired(); - - @Message( level = MessageLevel.ERROR, text = "At least one pac4j client must be defined") - public void atLeastOnePac4jClientMustBeDefined(); - - @Message( level = MessageLevel.ERROR, text = "Crypto service, alias service and cluster name required") - public void cryptoServiceAndAliasServiceAndClusterNameRequired(); - - @Message( level = MessageLevel.ERROR, text = "Unable to generate a password for encryption") - public void unableToGenerateAPasswordForEncryption(Exception e); - - @Message( level = MessageLevel.INFO, text = - "No private key passphrase alias found. Defaulting to master. Exception encountered: {0}") - public void noPrivateKeyPasshraseProvisioned(Exception e); -} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/deploy/Pac4jFederationProviderContributor.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/deploy/Pac4jFederationProviderContributor.java b/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/deploy/Pac4jFederationProviderContributor.java deleted file mode 100644 index c673ed5..0000000 --- a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/deploy/Pac4jFederationProviderContributor.java +++ /dev/null @@ -1,77 +0,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. - */ -package org.apache.hadoop.gateway.pac4j.deploy; - -import org.apache.hadoop.gateway.deploy.DeploymentContext; -import org.apache.hadoop.gateway.deploy.ProviderDeploymentContributorBase; -import org.apache.hadoop.gateway.descriptor.FilterParamDescriptor; -import org.apache.hadoop.gateway.descriptor.ResourceDescriptor; -import org.apache.hadoop.gateway.topology.Provider; -import org.apache.hadoop.gateway.topology.Service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Descriptor of the pac4j provider. This module must be deployed for the KnoxSSO service. - * - * @since 0.8.0 - */ -public class Pac4jFederationProviderContributor extends ProviderDeploymentContributorBase { - - private static final String ROLE = "federation"; - private static final String NAME = "pac4j"; - private static final String DISPATCHER_FILTER_CLASSNAME = "org.apache.hadoop.gateway.pac4j.filter.Pac4jDispatcherFilter"; - private static final String IDENTITY_ADAPTER_CLASSNAME = "org.apache.hadoop.gateway.pac4j.filter.Pac4jIdentityAdapter"; - - @Override - public String getRole() { - return ROLE; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public void initializeContribution(DeploymentContext context) { - super.initializeContribution(context); - } - - @Override - public void contributeProvider(DeploymentContext context, Provider provider) { - } - - @Override - public void contributeFilter(DeploymentContext context, Provider provider, Service service, - ResourceDescriptor resource, List<FilterParamDescriptor> params) { - // blindly add all the provider params as filter init params - if (params == null) { - params = new ArrayList<FilterParamDescriptor>(); - } - Map<String, String> providerParams = provider.getParams(); - for(Entry<String, String> entry : providerParams.entrySet()) { - params.add( resource.createFilterParam().name( entry.getKey() ).value( entry.getValue() ) ); - } - resource.addFilter().name( getName() ).role( getRole() ).impl( DISPATCHER_FILTER_CLASSNAME ).params( params ); - resource.addFilter().name( getName() ).role( getRole() ).impl( IDENTITY_ADAPTER_CLASSNAME ).params( params ); - } -} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/filter/Pac4jDispatcherFilter.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/filter/Pac4jDispatcherFilter.java b/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/filter/Pac4jDispatcherFilter.java deleted file mode 100644 index 574dea5..0000000 --- a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/filter/Pac4jDispatcherFilter.java +++ /dev/null @@ -1,215 +0,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. - */ -package org.apache.hadoop.gateway.pac4j.filter; - -import org.apache.hadoop.gateway.i18n.messages.MessagesFactory; -import org.apache.hadoop.gateway.pac4j.Pac4jMessages; -import org.apache.hadoop.gateway.pac4j.session.KnoxSessionStore; -import org.apache.hadoop.gateway.services.GatewayServices; -import org.apache.hadoop.gateway.services.security.KeystoreService; -import org.apache.hadoop.gateway.services.security.MasterService; -import org.apache.hadoop.gateway.services.security.AliasService; -import org.apache.hadoop.gateway.services.security.AliasServiceException; -import org.apache.hadoop.gateway.services.security.CryptoService; -import org.pac4j.config.client.PropertiesConfigFactory; -import org.pac4j.core.client.Client; -import org.pac4j.core.config.Config; -import org.pac4j.core.config.ConfigSingleton; -import org.pac4j.core.context.J2EContext; -import org.pac4j.core.context.Pac4jConstants; -import org.pac4j.core.util.CommonHelper; -import org.pac4j.http.client.indirect.IndirectBasicAuthClient; -import org.pac4j.http.credentials.authenticator.test.SimpleTestUsernamePasswordAuthenticator; -import org.pac4j.j2e.filter.CallbackFilter; -import org.pac4j.j2e.filter.RequiresAuthenticationFilter; - -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * <p>This is the main filter for the pac4j provider. The pac4j provider module heavily relies on the j2e-pac4j library (https://github.com/pac4j/j2e-pac4j).</p> - * <p>This filter dispatches the HTTP calls between the j2e-pac4j filters:</p> - * <ul> - * <li>to the {@link CallbackFilter} if the <code>client_name</code> parameter exists: it finishes the authentication process</li> - * <li>to the {@link RequiresAuthenticationFilter} otherwise: it starts the authentication process (redirection to the identity provider) if the user is not authenticated</li> - * </ul> - * <p>It uses the {@link KnoxSessionStore} to manage session data. The generated cookies are defined on a domain name - * which can be configured via the domain suffix parameter: <code>pac4j.cookie.domain.suffix</code>.</p> - * <p>The callback url must be defined to the current protected url (KnoxSSO service for example) via the parameter: <code>pac4j.callbackUrl</code>.</p> - * - * @since 0.8.0 - */ -public class Pac4jDispatcherFilter implements Filter { - - private static Pac4jMessages log = MessagesFactory.get(Pac4jMessages.class); - - public static final String TEST_BASIC_AUTH = "testBasicAuth"; - - public static final String PAC4J_CALLBACK_URL = "pac4j.callbackUrl"; - - public static final String PAC4J_CALLBACK_PARAMETER = "pac4jCallback"; - - private static final String PAC4J_COOKIE_DOMAIN_SUFFIX_PARAM = "pac4j.cookie.domain.suffix"; - - private CallbackFilter callbackFilter; - - private RequiresAuthenticationFilter requiresAuthenticationFilter; - private MasterService masterService = null; - private KeystoreService keystoreService = null; - private AliasService aliasService = null; - - @Override - public void init( FilterConfig filterConfig ) throws ServletException { - // JWT service - final ServletContext context = filterConfig.getServletContext(); - CryptoService cryptoService = null; - String clusterName = null; - if (context != null) { - GatewayServices services = (GatewayServices) context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE); - clusterName = (String) context.getAttribute(GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE); - if (services != null) { - keystoreService = (KeystoreService) services.getService(GatewayServices.KEYSTORE_SERVICE); - cryptoService = (CryptoService) services.getService(GatewayServices.CRYPTO_SERVICE); - aliasService = (AliasService) services.getService(GatewayServices.ALIAS_SERVICE); - masterService = (MasterService) services.getService("MasterService"); - } - } - // crypto service, alias service and cluster name are mandatory - if (cryptoService == null || aliasService == null || clusterName == null) { - log.cryptoServiceAndAliasServiceAndClusterNameRequired(); - throw new ServletException("The crypto service, alias service and cluster name are required."); - } - try { - aliasService.getPasswordFromAliasForCluster(clusterName, KnoxSessionStore.PAC4J_PASSWORD, true); - } catch (AliasServiceException e) { - log.unableToGenerateAPasswordForEncryption(e); - throw new ServletException("Unable to generate a password for encryption."); - } - - // url to SSO authentication provider - String pac4jCallbackUrl = filterConfig.getInitParameter(PAC4J_CALLBACK_URL); - if (pac4jCallbackUrl == null) { - log.ssoAuthenticationProviderUrlRequired(); - throw new ServletException("Required pac4j callback URL is missing."); - } - // add the callback parameter to know it's a callback - pac4jCallbackUrl = CommonHelper.addParameter(pac4jCallbackUrl, PAC4J_CALLBACK_PARAMETER, "true"); - - final Config config; - final String clientName; - // client name from servlet parameter (mandatory) - final String clientNameParameter = filterConfig.getInitParameter(Pac4jConstants.CLIENT_NAME); - if (clientNameParameter == null) { - log.clientNameParameterRequired(); - throw new ServletException("Required pac4j clientName parameter is missing."); - } - if (TEST_BASIC_AUTH.equalsIgnoreCase(clientNameParameter)) { - // test configuration - final IndirectBasicAuthClient indirectBasicAuthClient = new IndirectBasicAuthClient(new SimpleTestUsernamePasswordAuthenticator()); - indirectBasicAuthClient.setRealmName("Knox TEST"); - config = new Config(pac4jCallbackUrl, indirectBasicAuthClient); - clientName = "IndirectBasicAuthClient"; - } else { - // get clients from the init parameters - final Map<String, String> properties = new HashMap<>(); - final Enumeration<String> names = filterConfig.getInitParameterNames(); - addDefaultConfig(clientNameParameter, properties); - while (names.hasMoreElements()) { - final String key = names.nextElement(); - properties.put(key, filterConfig.getInitParameter(key)); - } - final PropertiesConfigFactory propertiesConfigFactory = new PropertiesConfigFactory(pac4jCallbackUrl, properties); - config = propertiesConfigFactory.build(); - final List<Client> clients = config.getClients().getClients(); - if (clients == null || clients.size() == 0) { - log.atLeastOnePac4jClientMustBeDefined(); - throw new ServletException("At least one pac4j client must be defined."); - } - if (CommonHelper.isBlank(clientNameParameter)) { - clientName = clients.get(0).getName(); - } else { - clientName = clientNameParameter; - } - } - - callbackFilter = new CallbackFilter(); - requiresAuthenticationFilter = new RequiresAuthenticationFilter(); - requiresAuthenticationFilter.setClientName(clientName); - requiresAuthenticationFilter.setConfig(config); - - final String domainSuffix = filterConfig.getInitParameter(PAC4J_COOKIE_DOMAIN_SUFFIX_PARAM); - config.setSessionStore(new KnoxSessionStore(cryptoService, clusterName, domainSuffix)); - ConfigSingleton.setConfig(config); - } - - private void addDefaultConfig(String clientNameParameter, Map<String, String> properties) { - // add default saml params - if (clientNameParameter.contains("SAML2Client")) { - properties.put(PropertiesConfigFactory.SAML_KEYSTORE_PATH, - keystoreService.getKeystorePath()); - - properties.put(PropertiesConfigFactory.SAML_KEYSTORE_PASSWORD, - new String(masterService.getMasterSecret())); - - // check for provisioned alias for private key - char[] gip = null; - try { - gip = aliasService.getGatewayIdentityPassphrase(); - } - catch(AliasServiceException ase) { - log.noPrivateKeyPasshraseProvisioned(ase); - } - if (gip != null) { - properties.put(PropertiesConfigFactory.SAML_PRIVATE_KEY_PASSWORD, - new String(gip)); - } - else { - // no alias provisioned then use the master - properties.put(PropertiesConfigFactory.SAML_PRIVATE_KEY_PASSWORD, - new String(masterService.getMasterSecret())); - } - } - } - - @Override - public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - - final HttpServletRequest request = (HttpServletRequest) servletRequest; - final HttpServletResponse response = (HttpServletResponse) servletResponse; - final J2EContext context = new J2EContext(request, response, ConfigSingleton.getConfig().getSessionStore()); - - // it's a callback from an identity provider - if (request.getParameter(PAC4J_CALLBACK_PARAMETER) != null) { - // apply CallbackFilter - callbackFilter.doFilter(servletRequest, servletResponse, filterChain); - } else { - // otherwise just apply security and requires authentication - // apply RequiresAuthenticationFilter - requiresAuthenticationFilter.doFilter(servletRequest, servletResponse, filterChain); - } - } - - @Override - public void destroy() { } -} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/filter/Pac4jIdentityAdapter.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/filter/Pac4jIdentityAdapter.java b/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/filter/Pac4jIdentityAdapter.java deleted file mode 100644 index ec5c368..0000000 --- a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/filter/Pac4jIdentityAdapter.java +++ /dev/null @@ -1,132 +0,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. - */ -package org.apache.hadoop.gateway.pac4j.filter; - -import org.apache.hadoop.gateway.audit.api.*; -import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants; -import org.apache.hadoop.gateway.filter.AbstractGatewayFilter; -import org.apache.hadoop.gateway.security.PrimaryPrincipal; -import org.pac4j.core.config.ConfigSingleton; -import org.pac4j.core.context.J2EContext; -import org.pac4j.core.profile.ProfileManager; -import org.pac4j.core.profile.UserProfile; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.security.auth.Subject; -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; - -/** - * <p>This filter retrieves the authenticated user saved by the pac4j provider and injects it into the J2E HTTP request.</p> - * - * @since 0.8.0 - */ -public class Pac4jIdentityAdapter implements Filter { - - private static final Logger logger = LoggerFactory.getLogger(Pac4jIdentityAdapter.class); - - private static AuditService auditService = AuditServiceFactory.getAuditService(); - private static Auditor auditor = auditService.getAuditor( - AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME, - AuditConstants.KNOX_COMPONENT_NAME ); - - private String testIdentifier; - - @Override - public void init( FilterConfig filterConfig ) throws ServletException { - } - - public void destroy() { - } - - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) - throws IOException, ServletException { - - final HttpServletRequest request = (HttpServletRequest) servletRequest; - final HttpServletResponse response = (HttpServletResponse) servletResponse; - final J2EContext context = new J2EContext(request, response, ConfigSingleton.getConfig().getSessionStore()); - final ProfileManager manager = new ProfileManager(context); - final UserProfile profile = manager.get(true); - logger.debug("User authenticated as: {}", profile); - manager.remove(true); - final String id = profile.getId(); - testIdentifier = id; - PrimaryPrincipal pp = new PrimaryPrincipal(id); - Subject subject = new Subject(); - subject.getPrincipals().add(pp); - auditService.getContext().setUsername(id); - String sourceUri = (String)request.getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME ); - auditor.audit(Action.AUTHENTICATION, sourceUri, ResourceType.URI, ActionOutcome.SUCCESS); - - doAs(request, response, chain, subject); - } - - private void doAs(final ServletRequest request, - final ServletResponse response, final FilterChain chain, Subject subject) - throws IOException, ServletException { - try { - Subject.doAs( - subject, - new PrivilegedExceptionAction<Object>() { - 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); - } - } - } - - /** - * For tests only. - */ - public static void setAuditService(AuditService auditService) { - Pac4jIdentityAdapter.auditService = auditService; - } - - /** - * For tests only. - */ - public static void setAuditor(Auditor auditor) { - Pac4jIdentityAdapter.auditor = auditor; - } - - /** - * For tests only. - */ - public String getTestIdentifier() { - return testIdentifier; - } -} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/session/KnoxSessionStore.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/session/KnoxSessionStore.java b/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/session/KnoxSessionStore.java deleted file mode 100644 index 1d7c567..0000000 --- a/gateway-provider-security-pac4j/src/main/java/org/apache/hadoop/gateway/pac4j/session/KnoxSessionStore.java +++ /dev/null @@ -1,120 +0,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. - */ -package org.apache.hadoop.gateway.pac4j.session; - -import org.apache.commons.codec.binary.Base64; -import org.apache.hadoop.gateway.services.security.CryptoService; -import org.apache.hadoop.gateway.services.security.EncryptionResult; -import org.apache.hadoop.gateway.util.Urls; -import org.pac4j.core.context.ContextHelper; -import org.pac4j.core.context.Cookie; -import org.pac4j.core.context.WebContext; -import org.pac4j.core.context.session.SessionStore; -import org.pac4j.core.exception.TechnicalException; -import org.pac4j.core.util.JavaSerializationHelper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.Serializable; - -/** - * Specific session store where data are saved into cookies (and not in memory). - * Each data is encrypted and base64 encoded before being saved as a cookie (for security reasons). - * - * @since 0.8.0 - */ -public class KnoxSessionStore implements SessionStore { - - private static final Logger logger = LoggerFactory.getLogger(KnoxSessionStore.class); - - public static final String PAC4J_PASSWORD = "pac4j.password"; - - public static final String PAC4J_SESSION_PREFIX = "pac4j.session."; - - private final JavaSerializationHelper javaSerializationHelper; - - private final CryptoService cryptoService; - - private final String clusterName; - - private final String domainSuffix; - - public KnoxSessionStore(final CryptoService cryptoService, final String clusterName, final String domainSuffix) { - javaSerializationHelper = new JavaSerializationHelper(); - this.cryptoService = cryptoService; - this.clusterName = clusterName; - this.domainSuffix = domainSuffix; - } - - public String getOrCreateSessionId(WebContext context) { - return null; - } - - private Serializable decryptBase64(final String v) { - if (v != null && v.length() > 0) { - byte[] bytes = Base64.decodeBase64(v); - EncryptionResult result = EncryptionResult.fromByteArray(bytes); - byte[] clear = cryptoService.decryptForCluster(this.clusterName, - PAC4J_PASSWORD, - result.cipher, - result.iv, - result.salt); - if (clear != null) { - return javaSerializationHelper.unserializeFromBytes(clear); - } - } - return null; - } - - public Object get(WebContext context, String key) { - final Cookie cookie = ContextHelper.getCookie(context, PAC4J_SESSION_PREFIX + key); - Object value = null; - if (cookie != null) { - value = decryptBase64(cookie.getValue()); - } - logger.debug("Get from session: {} = {}", key, value); - return value; - } - - private String encryptBase64(final Object o) { - if (o == null || o.equals("")) { - return null; - } else { - final byte[] bytes = javaSerializationHelper.serializeToBytes((Serializable) o); - EncryptionResult result = cryptoService.encryptForCluster(this.clusterName, PAC4J_PASSWORD, bytes); - return Base64.encodeBase64String(result.toByteAray()); - } - } - - public void set(WebContext context, String key, Object value) { - logger.debug("Save in session: {} = {}", key, value); - final Cookie cookie = new Cookie(PAC4J_SESSION_PREFIX + key, encryptBase64(value)); - try { - String domain = Urls.getDomainName(context.getFullRequestURL(), this.domainSuffix); - if (domain == null) { - domain = context.getServerName(); - } - cookie.setDomain(domain); - } catch (final Exception e) { - throw new TechnicalException(e); - } - cookie.setHttpOnly(true); - cookie.setSecure(ContextHelper.isHttpsOrSecure(context)); - context.addResponseCookie(cookie); - } -} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/Pac4jMessages.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/Pac4jMessages.java b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/Pac4jMessages.java new file mode 100644 index 0000000..32e07e6 --- /dev/null +++ b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/Pac4jMessages.java @@ -0,0 +1,50 @@ +/** + * 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.knox.gateway.pac4j; + +import org.apache.knox.gateway.i18n.messages.Message; +import org.apache.knox.gateway.i18n.messages.MessageLevel; +import org.apache.knox.gateway.i18n.messages.Messages; + +/** + * Logging messages for the pac4j provider. + * + * @since 0.8.0 + */ +@Messages(logger="org.apache.hadoop.gateway.pac4j") +public interface Pac4jMessages { + + @Message( level = MessageLevel.ERROR, text = "pac4j callback URL required") + public void ssoAuthenticationProviderUrlRequired(); + + @Message( level = MessageLevel.ERROR, text = "pac4j clientName parameter required") + public void clientNameParameterRequired(); + + @Message( level = MessageLevel.ERROR, text = "At least one pac4j client must be defined") + public void atLeastOnePac4jClientMustBeDefined(); + + @Message( level = MessageLevel.ERROR, text = "Crypto service, alias service and cluster name required") + public void cryptoServiceAndAliasServiceAndClusterNameRequired(); + + @Message( level = MessageLevel.ERROR, text = "Unable to generate a password for encryption") + public void unableToGenerateAPasswordForEncryption(Exception e); + + @Message( level = MessageLevel.INFO, text = + "No private key passphrase alias found. Defaulting to master. Exception encountered: {0}") + public void noPrivateKeyPasshraseProvisioned(Exception e); +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/deploy/Pac4jFederationProviderContributor.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/deploy/Pac4jFederationProviderContributor.java b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/deploy/Pac4jFederationProviderContributor.java new file mode 100644 index 0000000..f027dd3 --- /dev/null +++ b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/deploy/Pac4jFederationProviderContributor.java @@ -0,0 +1,78 @@ +/** + * 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.knox.gateway.pac4j.deploy; + +import org.apache.knox.gateway.deploy.DeploymentContext; +import org.apache.knox.gateway.deploy.ProviderDeploymentContributorBase; +import org.apache.knox.gateway.descriptor.FilterParamDescriptor; +import org.apache.knox.gateway.descriptor.ResourceDescriptor; +import org.apache.knox.gateway.topology.Provider; +import org.apache.knox.gateway.topology.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Descriptor of the pac4j provider. This module must be deployed for the KnoxSSO service. + * + * @since 0.8.0 + */ +public class Pac4jFederationProviderContributor extends + ProviderDeploymentContributorBase { + + private static final String ROLE = "federation"; + private static final String NAME = "pac4j"; + private static final String DISPATCHER_FILTER_CLASSNAME = "Pac4jDispatcherFilter"; + private static final String IDENTITY_ADAPTER_CLASSNAME = "Pac4jIdentityAdapter"; + + @Override + public String getRole() { + return ROLE; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public void initializeContribution(DeploymentContext context) { + super.initializeContribution(context); + } + + @Override + public void contributeProvider(DeploymentContext context, Provider provider) { + } + + @Override + public void contributeFilter(DeploymentContext context, Provider provider, Service service, + ResourceDescriptor resource, List<FilterParamDescriptor> params) { + // blindly add all the provider params as filter init params + if (params == null) { + params = new ArrayList<FilterParamDescriptor>(); + } + Map<String, String> providerParams = provider.getParams(); + for(Entry<String, String> entry : providerParams.entrySet()) { + params.add( resource.createFilterParam().name( entry.getKey() ).value( entry.getValue() ) ); + } + resource.addFilter().name( getName() ).role( getRole() ).impl( DISPATCHER_FILTER_CLASSNAME ).params( params ); + resource.addFilter().name( getName() ).role( getRole() ).impl( IDENTITY_ADAPTER_CLASSNAME ).params( params ); + } +}