Repository: knox Updated Branches: refs/heads/master 44e2bfc8a -> 2135bc22c
KNOX-1549 - KnoxSSO should support signing keys per topology Signed-off-by: Kevin Risden <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/2135bc22 Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/2135bc22 Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/2135bc22 Branch: refs/heads/master Commit: 2135bc22c0b5beb383450d6a574bcc21078e4b19 Parents: 44e2bfc Author: Kevin Risden <[email protected]> Authored: Thu Nov 1 16:50:21 2018 -0400 Committer: Kevin Risden <[email protected]> Committed: Wed Nov 7 11:37:14 2018 -0500 ---------------------------------------------------------------------- .../federation/AbstractJWTFilterTest.java | 34 ++-- .../security/impl/DefaultKeystoreService.java | 32 ++- .../impl/DefaultTokenAuthorityService.java | 51 +++-- .../impl/DefaultTokenAuthorityServiceTest.java | 56 ++++++ .../resources/keystores/testSigningKeyName.jks | Bin 0 -> 2238 bytes .../gateway/service/knoxsso/WebSSOResource.java | 76 ++++--- .../service/knoxsso/WebSSOResourceTest.java | 199 ++++++++++++++----- .../knoxtoken/TokenServiceResourceTest.java | 52 +++-- .../services/security/KeystoreService.java | 36 ++-- .../security/token/JWTokenAuthority.java | 6 +- 10 files changed, 370 insertions(+), 172 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/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 index ea8607d..387b274 100644 --- 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 @@ -31,7 +31,6 @@ import org.apache.knox.gateway.provider.federation.jwt.filter.SSOCookieFederatio 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.easymock.EasyMock; import org.junit.After; @@ -48,7 +47,6 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; import java.net.InetAddress; import java.nio.charset.StandardCharsets; import java.security.AccessController; @@ -107,7 +105,7 @@ public abstract class AbstractJWTFilterTest { } @After - public void teardown() throws Exception { + public void teardown() { handler.destroy(); } @@ -675,7 +673,7 @@ public abstract class AbstractJWTFilterTest { TestFilterChain chain = new TestFilterChain(); handler.doFilter(request, response, chain); Assert.assertTrue("doFilterCalled should not be false.", !chain.doFilterCalled); - Assert.assertTrue("No Subject should be returned.", chain.subject == null); + Assert.assertNull("No Subject should be returned.", chain.subject); } catch (ServletException se) { fail("Should NOT have thrown a ServletException."); } @@ -765,51 +763,56 @@ public abstract class AbstractJWTFilterTest { private PublicKey verifyingKey; - public TestJWTokenAuthority(PublicKey verifyingKey) { + TestJWTokenAuthority(PublicKey verifyingKey) { this.verifyingKey = verifyingKey; } @Override - public JWT issueToken(Subject subject, String algorithm) throws TokenServiceException { + public JWT issueToken(Subject subject, String algorithm) { return null; } @Override - public JWT issueToken(Principal p, String algorithm) throws TokenServiceException { + public JWT issueToken(Principal p, String algorithm) { return null; } @Override - public JWT issueToken(Principal p, String audience, String algorithm) - throws TokenServiceException { + public JWT issueToken(Principal p, String audience, String algorithm) { return null; } @Override - public boolean verifyToken(JWT token) throws TokenServiceException { + public boolean verifyToken(JWT token) { JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) verifyingKey); return token.verify(verifier); } @Override public JWT issueToken(Principal p, String audience, String algorithm, - long expires) throws TokenServiceException { + long expires) { return null; } @Override public JWT issueToken(Principal p, List<String> audiences, String algorithm, - long expires) throws TokenServiceException { + long expires) { + return null; + } + + @Override + public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires, + String signingKeystoreName, String signingKeystoreAlias, char[] signingKeystorePassphrase) { return null; } @Override - public JWT issueToken(Principal p, String algorithm, long expires) throws TokenServiceException { + public JWT issueToken(Principal p, String algorithm, long expires) { return null; } @Override - public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException { + public boolean verifyToken(JWT token, RSAPublicKey publicKey) { JWSVerifier verifier = new RSASSAVerifier(publicKey); return token.verify(verifier); } @@ -820,8 +823,7 @@ public abstract class AbstractJWTFilterTest { Subject subject = null; @Override - public void doFilter(ServletRequest request, ServletResponse response) - throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response) { doFilterCalled = true; subject = Subject.getSubject( AccessController.getContext() ); http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java index 26ca369..08257ca 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java @@ -144,16 +144,23 @@ public class DefaultKeystoreService extends BaseKeystoreService implements @Override public KeyStore getSigningKeystore() throws KeystoreServiceException { - File keyStoreFile = null; - if (signingKeystoreName == null) { + return getSigningKeystore(null); + } + + @Override + public KeyStore getSigningKeystore(String keystoreName) throws KeystoreServiceException { + File keyStoreFile; + if(keystoreName != null) { + keyStoreFile = new File(keyStoreDir + keystoreName + ".jks"); + } else if (signingKeystoreName != null) { + keyStoreFile = new File(keyStoreDir + signingKeystoreName); + } else { keyStoreFile = new File(keyStoreDir + GATEWAY_KEYSTORE); } - else { - keyStoreFile = new File(keyStoreDir + signingKeystoreName); - // make sure the keystore exists - if (!keyStoreFile.exists()) { - throw new KeystoreServiceException("Configured signing keystore does not exist."); - } + + // make sure the keystore exists + if (!keyStoreFile.exists()) { + throw new KeystoreServiceException("Configured signing keystore does not exist."); } readLock.lock(); try { @@ -305,10 +312,15 @@ public class DefaultKeystoreService extends BaseKeystoreService implements @Override public Key getSigningKey(String alias, char[] passphrase) throws KeystoreServiceException { + return getSigningKey(null, alias, passphrase); + } + + @Override + public Key getSigningKey(String keystoreName, String alias, char[] passphrase) throws KeystoreServiceException { Key key = null; readLock.lock(); try { - KeyStore ks = getSigningKeystore(); + KeyStore ks = getSigningKeystore(keystoreName); if (passphrase == null) { passphrase = masterService.getMasterSecret(); LOG.assumingKeyPassphraseIsMaster(); @@ -331,7 +343,7 @@ public class DefaultKeystoreService extends BaseKeystoreService implements } } - public KeyStore getCredentialStoreForCluster(String clusterName) + public KeyStore getCredentialStoreForCluster(String clusterName) throws KeystoreServiceException { final File keyStoreFile = new File( keyStoreDir + clusterName + CREDENTIALS_SUFFIX ); readLock.lock(); http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java index ad8f999..b32e914 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java @@ -110,6 +110,13 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { @Override public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires) throws TokenServiceException { + return issueToken(p, audiences, algorithm, expires, null, null, null); + } + + @Override + public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires, + String signingKeystoreName, String signingKeystoreAlias, char[] signingKeystorePassphrase) + throws TokenServiceException { String[] claimArray = new String[4]; claimArray[0] = "KNOXSSO"; claimArray[1] = p.getName(); @@ -121,19 +128,18 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { claimArray[3] = String.valueOf(expires); } - JWT token = null; + JWT token; if (SUPPORTED_SIG_ALGS.contains(algorithm)) { token = new JWTToken(algorithm, claimArray, audiences); - RSAPrivateKey key; - char[] passphrase = null; + char[] passphrase; try { - passphrase = getSigningKeyPassphrase(); + passphrase = getSigningKeyPassphrase(signingKeystorePassphrase); } catch (AliasServiceException e) { throw new TokenServiceException(e); } try { - key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(), - passphrase); + RSAPrivateKey key = (RSAPrivateKey) ks.getSigningKey(signingKeystoreName, + getSigningKeyAlias(signingKeystoreAlias), passphrase); JWSSigner signer = new RSASSASigner(key); token.sign(signer); } catch (KeystoreServiceException e) { @@ -147,7 +153,10 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { return token; } - private char[] getSigningKeyPassphrase() throws AliasServiceException { + private char[] getSigningKeyPassphrase(char[] signingKeyPassphrase) throws AliasServiceException { + if(signingKeyPassphrase != null) { + return signingKeyPassphrase; + } char[] phrase = as.getPasswordFromAliasForGateway(SIGNING_KEY_PASSPHRASE); if (phrase == null) { phrase = as.getGatewayIdentityPassphrase(); @@ -155,11 +164,14 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { return phrase; } - private String getSigningKeyAlias() { - if (signingKeyAlias == null) { - return "gateway-identity"; + private String getSigningKeyAlias(String signingKeystoreAlias) { + if(signingKeystoreAlias != null) { + return signingKeystoreAlias; + } + if(signingKeyAlias != null) { + return signingKeyAlias; } - return signingKeyAlias; + return "gateway-identity"; } @Override @@ -171,11 +183,11 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { @Override public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException { - boolean rc = false; + boolean rc; PublicKey key; try { if (publicKey == null) { - key = ks.getSigningKeystore().getCertificate(getSigningKeyAlias()).getPublicKey(); + key = ks.getSigningKeystore().getCertificate(getSigningKeyAlias(signingKeyAlias)).getPublicKey(); } else { key = publicKey; @@ -184,9 +196,7 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { // TODO: interrogate the token for issuer claim in order to determine the public key to use for verification // consider jwk for specifying the key too rc = token.verify(verifier); - } catch (KeyStoreException e) { - throw new TokenServiceException("Cannot verify token.", e); - } catch (KeystoreServiceException e) { + } catch (KeyStoreException | KeystoreServiceException e) { throw new TokenServiceException("Cannot verify token.", e); } return rc; @@ -200,21 +210,18 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { } signingKeyAlias = config.getSigningKeyAlias(); - @SuppressWarnings("unused") RSAPrivateKey key; - char[] passphrase = null; + char[] passphrase; try { passphrase = as.getPasswordFromAliasForGateway(SIGNING_KEY_PASSPHRASE); if (passphrase != null) { - key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(), + key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(signingKeyAlias), passphrase); if (key == null) { throw new ServiceLifecycleException("Provisioned passphrase cannot be used to acquire signing key."); } } - } catch (AliasServiceException e) { - throw new ServiceLifecycleException("Provisioned signing key passphrase cannot be acquired.", e); - } catch (KeystoreServiceException e) { + } catch (AliasServiceException | KeystoreServiceException e) { throw new ServiceLifecycleException("Provisioned signing key passphrase cannot be acquired.", e); } } http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java index f4d2d68..a51c79f 100644 --- a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java +++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java @@ -19,6 +19,8 @@ package org.apache.knox.gateway.services.token.impl; import java.io.File; import java.security.Principal; +import java.security.interfaces.RSAPublicKey; +import java.util.Collections; import java.util.HashMap; import org.apache.knox.gateway.config.GatewayConfig; @@ -251,4 +253,58 @@ public class DefaultTokenAuthorityServiceTest extends org.junit.Assert { } } + @Test + public void testTokenCreationCustomSigningKey() throws Exception { + /* + Generated testSigningKeyName.jks with the following commands: + cd gateway-server/src/test/resources/keystores/ + keytool -genkey -alias testSigningKeyAlias -keyalg RSA -keystore testSigningKeyName.jks \ + -storepass testSigningKeyPassphrase -keypass testSigningKeyPassphrase -keysize 2048 \ + -dname 'CN=testSigningKey,OU=example,O=Apache,L=US,ST=CA,C=US' -noprompt + */ + + String customSigningKeyName = "testSigningKeyName"; + String customSigningKeyAlias = "testSigningKeyAlias"; + String customSigningKeyPassphrase = "testSigningKeyPassphrase"; + + Principal principal = EasyMock.createNiceMock(Principal.class); + EasyMock.expect(principal.getName()).andReturn("[email protected]"); + + GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); + String basedir = System.getProperty("basedir"); + if (basedir == null) { + basedir = new File(".").getCanonicalPath(); + } + + EasyMock.expect(config.getGatewaySecurityDir()).andReturn(basedir + "/target/test-classes"); + EasyMock.expect(config.getSigningKeystoreName()).andReturn("server-keystore.jks"); + EasyMock.expect(config.getSigningKeyAlias()).andReturn("server").anyTimes(); + + MasterService ms = EasyMock.createNiceMock(MasterService.class); + EasyMock.expect(ms.getMasterSecret()).andReturn("horton".toCharArray()); + + AliasService as = EasyMock.createNiceMock(AliasService.class); + EasyMock.expect(as.getGatewayIdentityPassphrase()).andReturn("horton".toCharArray()); + + EasyMock.replay(principal, config, ms, as); + + DefaultKeystoreService ks = new DefaultKeystoreService(); + ks.setMasterService(ms); + ks.init(config, new HashMap<>()); + + DefaultTokenAuthorityService ta = new DefaultTokenAuthorityService(); + ta.setAliasService(as); + ta.setKeystoreService(ks); + ta.init(config, new HashMap<>()); + + JWT token = ta.issueToken(principal, Collections.emptyList(), "RS256", -1, + customSigningKeyName, customSigningKeyAlias, customSigningKeyPassphrase.toCharArray()); + assertEquals("KNOXSSO", token.getIssuer()); + assertEquals("[email protected]", token.getSubject()); + + RSAPublicKey customPublicKey = (RSAPublicKey)ks.getSigningKeystore(customSigningKeyName) + .getCertificate(customSigningKeyAlias).getPublicKey(); + assertFalse(ta.verifyToken(token)); + assertTrue(ta.verifyToken(token, customPublicKey)); + } } http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-server/src/test/resources/keystores/testSigningKeyName.jks ---------------------------------------------------------------------- diff --git a/gateway-server/src/test/resources/keystores/testSigningKeyName.jks b/gateway-server/src/test/resources/keystores/testSigningKeyName.jks new file mode 100644 index 0000000..d5e984a Binary files /dev/null and b/gateway-server/src/test/resources/keystores/testSigningKeyName.jks differ http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java ---------------------------------------------------------------------- diff --git a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java index 8f8002d..ab3702b 100644 --- a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java +++ b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java @@ -47,6 +47,8 @@ import javax.ws.rs.WebApplicationException; import org.apache.knox.gateway.audit.log4j.audit.Log4jAuditor; import org.apache.knox.gateway.i18n.messages.MessagesFactory; import org.apache.knox.gateway.services.GatewayServices; +import org.apache.knox.gateway.services.security.AliasService; +import org.apache.knox.gateway.services.security.AliasServiceException; 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; @@ -56,6 +58,7 @@ import org.apache.knox.gateway.util.WhitelistUtils; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.MediaType.APPLICATION_XML; +import static org.apache.knox.gateway.services.GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE; @Path( WebSSOResource.RESOURCE_PATH ) public class WebSSOResource { @@ -68,6 +71,10 @@ public class WebSSOResource { private static final String SSO_COOKIE_TOKEN_SIG_ALG = "knoxsso.token.sigalg"; private static final String SSO_COOKIE_TOKEN_WHITELIST_PARAM = "knoxsso.redirect.whitelist.regex"; + private static final String SSO_SIGNINGKEY_KEYSTORE_NAME = "knoxsso.signingkey.keystore.name"; + private static final String SSO_SIGNINGKEY_KEYSTORE_ALIAS = "knoxsso.signingkey.keystore.alias"; + private static final String SSO_SIGNINGKEY_KEYSTORE_PASSPHRASE_ALIAS = "knoxsso.signingkey.keystore.passphrase.alias"; + /* parameters expected by knoxsso */ private static final String SSO_EXPECTED_PARAM = "knoxsso.expected.params"; @@ -88,6 +95,7 @@ public class WebSSOResource { private boolean enableSession = false; private String signatureAlgorithm = "RS256"; private List<String> ssoExpectedparams = new ArrayList<>(); + private String clusterName = null; @Context HttpServletRequest request; @@ -100,19 +108,34 @@ public class WebSSOResource { @PostConstruct public void init() { + clusterName = String.valueOf(context.getAttribute(GATEWAY_CLUSTER_ATTRIBUTE)); + + handleCookieSetup(); + + String enableSessionStr = context.getInitParameter(SSO_ENABLE_SESSION_PARAM); + this.enableSession = Boolean.parseBoolean(enableSessionStr); + + String sigAlg = context.getInitParameter(SSO_COOKIE_TOKEN_SIG_ALG); + if (sigAlg != null) { + signatureAlgorithm = sigAlg; + } + + final String expectedParams = context.getInitParameter(SSO_EXPECTED_PARAM); + if (expectedParams != null) { + ssoExpectedparams = Arrays.asList(expectedParams.split(",")); + } + } - // configured cookieName + private void handleCookieSetup() { cookieName = context.getInitParameter(SSO_COOKIE_NAME); if (cookieName == null) { cookieName = DEFAULT_SSO_COOKIE_NAME; } String secure = context.getInitParameter(SSO_COOKIE_SECURE_ONLY_INIT_PARAM); - if (secure != null) { - secureOnly = ("false".equals(secure) ? false : true); - if (!secureOnly) { - log.cookieSecureOnly(secureOnly); - } + secureOnly = Boolean.parseBoolean(secure); + if (!secureOnly) { + log.cookieSecureOnly(secureOnly); } String age = context.getInitParameter(SSO_COOKIE_MAX_AGE_INIT_PARAM); @@ -136,8 +159,8 @@ public class WebSSOResource { String audiences = context.getInitParameter(SSO_COOKIE_TOKEN_AUDIENCES_PARAM); if (audiences != null) { String[] auds = audiences.split(","); - for (int i = 0; i < auds.length; i++) { - targetAudiences.add(auds[i].trim()); + for (String aud : auds) { + targetAudiences.add(aud.trim()); } } @@ -154,19 +177,6 @@ public class WebSSOResource { log.invalidTokenTTLEncountered(ttl); } } - - String enableSession = context.getInitParameter(SSO_ENABLE_SESSION_PARAM); - this.enableSession = ("true".equals(enableSession)); - - String sigAlg = context.getInitParameter(SSO_COOKIE_TOKEN_SIG_ALG); - if (sigAlg != null) { - signatureAlgorithm = sigAlg; - } - - final String expectedParams = context.getInitParameter(SSO_EXPECTED_PARAM); - if (expectedParams != null) { - ssoExpectedparams = Arrays.asList(expectedParams.split(",")); - } } @GET @@ -185,7 +195,7 @@ public class WebSSOResource { GatewayServices services = (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE); boolean removeOriginalUrlCookie = true; - String original = getCookieValue((HttpServletRequest) request, ORIGINAL_URL_COOKIE_NAME); + String original = getCookieValue(request, ORIGINAL_URL_COOKIE_NAME); if (original == null) { // in the case where there are no SAML redirects done before here // we need to get it from the request parameters @@ -218,17 +228,22 @@ public class WebSSOResource { } } + AliasService as = services.getService(GatewayServices.ALIAS_SERVICE); JWTokenAuthority ts = services.getService(GatewayServices.TOKEN_SERVICE); Principal p = request.getUserPrincipal(); try { - JWT token = null; - if (targetAudiences.isEmpty()) { - token = ts.issueToken(p, signatureAlgorithm, getExpiry()); - } else { - token = ts.issueToken(p, targetAudiences, signatureAlgorithm, getExpiry()); + String signingKeystoreName = context.getInitParameter(SSO_SIGNINGKEY_KEYSTORE_NAME); + String signingKeystoreAlias = context.getInitParameter(SSO_SIGNINGKEY_KEYSTORE_ALIAS); + String signingKeystorePassphraseAlias = context.getInitParameter(SSO_SIGNINGKEY_KEYSTORE_PASSPHRASE_ALIAS); + char[] signingKeystorePassphrase = null; + if(signingKeystorePassphraseAlias != null) { + signingKeystorePassphrase = as.getPasswordFromAliasForCluster(clusterName, signingKeystorePassphraseAlias); } + JWT token = ts.issueToken(p, targetAudiences, signatureAlgorithm, getExpiry(), + signingKeystoreName, signingKeystoreAlias, signingKeystorePassphrase); + // Coverity CID 1327959 if( token != null ) { addJWTHadoopCookie( original, token ); @@ -246,8 +261,7 @@ public class WebSSOResource { } catch (IOException e) { log.unableToCloseOutputStream(e.getMessage(), Arrays.toString(e.getStackTrace())); } - } - catch (TokenServiceException e) { + } catch (TokenServiceException| AliasServiceException e) { log.unableToIssueToken(e); } URI location = null; @@ -272,7 +286,7 @@ public class WebSSOResource { private String getOriginalUrlFromQueryParams() { String original = request.getParameter(ORIGINAL_URL_REQUEST_PARAM); - StringBuffer buf = new StringBuffer(original); + StringBuilder buf = new StringBuilder(original); boolean first = true; @@ -310,7 +324,7 @@ public class WebSSOResource { } private long getExpiry() { - long expiry = 0l; + long expiry; if (tokenTTL == -1) { expiry = -1; } http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java ---------------------------------------------------------------------- diff --git a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java index d719404..e5f972d 100644 --- a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java +++ b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java @@ -20,7 +20,10 @@ package org.apache.knox.gateway.service.knoxsso; import org.apache.http.HttpStatus; import org.apache.knox.gateway.audit.log4j.audit.Log4jAuditor; import org.apache.knox.gateway.config.GatewayConfig; +import org.apache.knox.gateway.services.security.AliasService; import org.apache.knox.gateway.util.RegExUtils; + +import static org.apache.knox.gateway.services.GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -31,7 +34,6 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.KeyPair; import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; @@ -73,21 +75,21 @@ import com.nimbusds.jose.crypto.RSASSAVerifier; */ public class WebSSOResourceTest { - protected static RSAPublicKey publicKey; - protected static RSAPrivateKey privateKey; + private static RSAPublicKey gatewayPublicKey; + private static RSAPrivateKey gatewayPrivateKey; @BeforeClass - public static void setup() throws Exception, NoSuchAlgorithmException { + public static void setup() throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(1024); - KeyPair KPair = kpg.generateKeyPair(); + KeyPair keyPair = kpg.generateKeyPair(); - publicKey = (RSAPublicKey) KPair.getPublic(); - privateKey = (RSAPrivateKey) KPair.getPrivate(); + gatewayPublicKey = (RSAPublicKey) keyPair.getPublic(); + gatewayPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); } @Test - public void testWhitelistMatching() throws Exception { + public void testWhitelistMatching() { String whitelist = "^https?://.*example.com:8080/.*$;" + "^https?://.*example.com/.*$;" + "^https?://.*example2.com:\\d{0,9}/.*$;" + @@ -144,7 +146,7 @@ public class WebSSOResourceTest { HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); Principal principal = EasyMock.createNiceMock(Principal.class); @@ -154,7 +156,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -196,7 +198,7 @@ public class WebSSOResourceTest { HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); Principal principal = EasyMock.createNiceMock(Principal.class); @@ -206,7 +208,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -254,7 +256,7 @@ public class WebSSOResourceTest { HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); Principal principal = EasyMock.createNiceMock(Principal.class); @@ -264,7 +266,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -313,7 +315,7 @@ public class WebSSOResourceTest { HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); Principal principal = EasyMock.createNiceMock(Principal.class); @@ -323,7 +325,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -366,7 +368,7 @@ public class WebSSOResourceTest { HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); Principal principal = EasyMock.createNiceMock(Principal.class); @@ -376,7 +378,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -422,7 +424,7 @@ public class WebSSOResourceTest { HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); Principal principal = EasyMock.createNiceMock(Principal.class); @@ -432,7 +434,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -480,7 +482,7 @@ public class WebSSOResourceTest { HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); Principal principal = EasyMock.createNiceMock(Principal.class); @@ -490,7 +492,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -537,7 +539,7 @@ public class WebSSOResourceTest { HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); Principal principal = EasyMock.createNiceMock(Principal.class); @@ -546,8 +548,8 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -601,7 +603,7 @@ public class WebSSOResourceTest { EasyMock.expect(request.getParameter("originalUrl")).andReturn( URLEncoder.encode("http://disallowedhost:9080/service", StandardCharsets.UTF_8.name())); EasyMock.expect(request.getAttribute("targetServiceRole")).andReturn("KNOXSSO").anyTimes(); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); EasyMock.expect(request.getServerName()).andReturn("localhost").anyTimes(); @@ -612,7 +614,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -636,7 +638,6 @@ public class WebSSOResourceTest { } } - @Test public void testTopologyDefinedWhitelist() throws Exception { final String testServiceRole = "TEST"; @@ -659,7 +660,7 @@ public class WebSSOResourceTest { HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); - EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap()); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); EasyMock.expect(request.getAttribute("targetServiceRole")).andReturn(testServiceRole).anyTimes(); EasyMock.expect(request.getServerName()).andReturn("localhost").anyTimes(); @@ -671,7 +672,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -694,7 +695,7 @@ public class WebSSOResourceTest { } @Test - public void testExpectedKnoxSSOParams() throws Exception { + public void testExpectedKnoxSSOParams() { final HashMap<String, String[]> paramMap = new HashMap<>(); paramMap.put("knoxtoken", new String[]{"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiO" @@ -723,7 +724,7 @@ public class WebSSOResourceTest { GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services).anyTimes(); - JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); + JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority).anyTimes(); HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); @@ -759,7 +760,7 @@ public class WebSSOResourceTest { } @Test - public void testRedactToken() throws Exception { + public void testRedactToken() { final String token = "eyJhbGciOiJSUzI1NiJ9." + "eyJzdWIiOiJhZG1pbjEiLCJpc3MiOiJLTk9YU1NPIiwiZXhwIjoxNTMwNzkwMjkxfQ." @@ -787,7 +788,85 @@ public class WebSSOResourceTest { "&originalUrl=http://www.local.com:8443/?gateway=one&knoxtoken", Log4jAuditor.maskTokenFromURL(fragment2)); assertEquals("/gateway/knoxsso/api/v1/websso?test=value"+ "&originalUrl=http://www.local.com:8443/?gateway=one&knoxtoken", Log4jAuditor.maskTokenFromURL(fragment3)); + } + + @Test + public void testCustomSigningKey() throws Exception { + + String topologyName = "testCustomSigningKeyTopology"; + String customSigningKeyName = "testSigningKeyName"; + String customSigningKeyAlias = "testSigningKeyAlias"; + String customSigningKeyPassphraseAlias = "testSigningKeyPassphraseAlias"; + String customSigningKeyPassphrase = "testSigningKeyPassphrase"; + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(1024); + KeyPair keyPair = kpg.generateKeyPair(); + RSAPublicKey customPublicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey customPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); + + ServletContext context = EasyMock.createNiceMock(ServletContext.class); + EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null); + EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null); + EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null); + EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null); + EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null); + EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(null); + EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(null); + EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null); + EasyMock.expect(context.getInitParameter("knoxsso.signingkey.keystore.name")) + .andReturn(customSigningKeyName); + EasyMock.expect(context.getInitParameter("knoxsso.signingkey.keystore.alias")) + .andReturn(customSigningKeyAlias); + EasyMock.expect(context.getInitParameter("knoxsso.signingkey.keystore.passphrase.alias")) + .andReturn(customSigningKeyPassphraseAlias); + EasyMock.expect(context.getAttribute(GATEWAY_CLUSTER_ATTRIBUTE)).andReturn(topologyName); + + HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); + EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service"); + EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap()); + EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); + + Principal principal = EasyMock.createNiceMock(Principal.class); + EasyMock.expect(principal.getName()).andReturn("alice").anyTimes(); + EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes(); + + GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); + EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); + + TestJWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey); + authority.addCustomSigningKey(customSigningKeyName, customSigningKeyAlias, customSigningKeyPassphrase.toCharArray(), + customPrivateKey); + EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); + + AliasService aliasService = EasyMock.createNiceMock(AliasService.class); + EasyMock.expect(aliasService.getPasswordFromAliasForCluster(topologyName, customSigningKeyPassphraseAlias)) + .andReturn(customSigningKeyPassphrase.toCharArray()); + EasyMock.expect(services.getService(GatewayServices.ALIAS_SERVICE)).andReturn(aliasService); + + HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); + ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class); + CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream); + + EasyMock.replay(principal, services, context, request, aliasService); + + WebSSOResource webSSOResponse = new WebSSOResource(); + webSSOResponse.request = request; + webSSOResponse.response = responseWrapper; + webSSOResponse.context = context; + webSSOResponse.init(); + // Issue a token + webSSOResponse.doGet(); + + // Check the cookie + Cookie cookie = responseWrapper.getCookie("hadoop-jwt"); + assertNotNull(cookie); + + JWT parsedToken = new JWTToken(cookie.getValue()); + assertEquals("alice", parsedToken.getSubject()); + assertFalse(authority.verifyToken(parsedToken, gatewayPublicKey)); + assertTrue(authority.verifyToken(parsedToken, customPublicKey)); } /** @@ -798,11 +877,7 @@ public class WebSSOResourceTest { private ServletOutputStream outputStream; private Map<String, Cookie> cookies = new HashMap<>(); - public CookieResponseWrapper(HttpServletResponse response) { - super(response); - } - - public CookieResponseWrapper(HttpServletResponse response, ServletOutputStream outputStream) { + CookieResponseWrapper(HttpServletResponse response, ServletOutputStream outputStream) { super(response); this.outputStream = outputStream; } @@ -818,22 +893,31 @@ public class WebSSOResourceTest { cookies.put(cookie.getName(), cookie); } - public Cookie getCookie(String name) { + Cookie getCookie(String name) { return cookies.get(name); } - } private static class TestJWTokenAuthority implements JWTokenAuthority { - private RSAPublicKey publicKey; private RSAPrivateKey privateKey; + private Map<String, Map<String,Object>> customSigningKeys = new HashMap<>(); - public TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) { + TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) { this.publicKey = publicKey; this.privateKey = privateKey; } + void addCustomSigningKey(String signingKeystoreName, String signingKeystoreAlias, + char[] signingKeystorePassphrase, RSAPrivateKey customPrivateKey) { + + Map<String, Object> signingKey = new HashMap<>(); + signingKey.put("alias", signingKeystoreAlias); + signingKey.put("passphrase", signingKeystorePassphrase); + signingKey.put("privateKey", customPrivateKey); + customSigningKeys.put(signingKeystoreName, signingKey); + } + @Override public JWT issueToken(Subject subject, String algorithm) throws TokenServiceException { @@ -854,7 +938,7 @@ public class WebSSOResourceTest { } @Override - public boolean verifyToken(JWT token) throws TokenServiceException { + public boolean verifyToken(JWT token) { JWSVerifier verifier = new RSASSAVerifier(publicKey); return token.verify(verifier); } @@ -872,7 +956,14 @@ public class WebSSOResourceTest { @Override public JWT issueToken(Principal p, List<String> audiences, String algorithm, - long expires) throws TokenServiceException { + long expires) throws TokenServiceException { + return issueToken(p, audiences, algorithm, expires, null, null, null); + } + + @Override + public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires, + String signingKeystoreName, String signingKeystoreAlias, char[] signingKeystorePassphrase) + throws TokenServiceException { String[] claimArray = new String[4]; claimArray[0] = "KNOXSSO"; claimArray[1] = p.getName(); @@ -884,24 +975,36 @@ public class WebSSOResourceTest { } JWT token = new JWTToken(algorithm, claimArray, audiences); + RSAPrivateKey privateKey = getPrivateKey(signingKeystoreName, signingKeystoreAlias, signingKeystorePassphrase); JWSSigner signer = new RSASSASigner(privateKey); token.sign(signer); return token; } + private RSAPrivateKey getPrivateKey(String signingKeystoreName, String signingKeystoreAlias, + char[] signingKeystorePassphrase) throws TokenServiceException { + if(signingKeystoreName != null) { + Map<String, Object> signingKey = customSigningKeys.get(signingKeystoreName); + if(signingKey == null || !signingKey.get("alias").equals(signingKeystoreAlias) || + !Arrays.equals((char[])signingKey.get("passphrase"), signingKeystorePassphrase)) { + throw new TokenServiceException("Invalid alias or passphrase"); + } + return (RSAPrivateKey)signingKey.get("privateKey"); + } + return privateKey; + } + @Override public JWT issueToken(Principal p, String algorithm, long expiry) throws TokenServiceException { - return issueToken(p, Collections.<String>emptyList(), algorithm, expiry); + return issueToken(p, Collections.emptyList(), algorithm, expiry); } @Override - public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException { + public boolean verifyToken(JWT token, RSAPublicKey publicKey) { JWSVerifier verifier = new RSASSAVerifier(publicKey); return token.verify(verifier); } - } - } http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java ---------------------------------------------------------------------- diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java index decef70..18a645f 100644 --- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java +++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java @@ -24,7 +24,6 @@ import com.nimbusds.jose.crypto.RSASSAVerifier; import org.apache.knox.gateway.security.PrimaryPrincipal; import org.apache.knox.gateway.services.GatewayServices; 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; @@ -38,7 +37,6 @@ import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; import java.security.KeyPair; import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; @@ -60,11 +58,11 @@ import static org.junit.Assert.assertTrue; */ public class TokenServiceResourceTest { - protected static RSAPublicKey publicKey; - protected static RSAPrivateKey privateKey; + private static RSAPublicKey publicKey; + private static RSAPrivateKey privateKey; @BeforeClass - public static void setup() throws Exception, NoSuchAlgorithmException { + public static void setup() throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(1024); KeyPair KPair = kpg.generateKeyPair(); @@ -74,25 +72,25 @@ public class TokenServiceResourceTest { } @Test - public void testTokenService() throws Exception { + public void testTokenService() { Assert.assertTrue(true); } @Test - public void testClientData() throws Exception { + public void testClientData() { TokenResource tr = new TokenResource(); Map<String,Object> clientDataMap = new HashMap<>(); tr.addClientDataToMap("cookie.name=hadoop-jwt,test=value".split(","), clientDataMap); - Assert.assertTrue(clientDataMap.size() == 2); + Assert.assertEquals(2, clientDataMap.size()); clientDataMap = new HashMap<>(); tr.addClientDataToMap("cookie.name=hadoop-jwt".split(","), clientDataMap); - Assert.assertTrue(clientDataMap.size() == 1); + Assert.assertEquals(1, clientDataMap.size()); clientDataMap = new HashMap<>(); tr.addClientDataToMap("".split(","), clientDataMap); - Assert.assertTrue(clientDataMap.size() == 0); + Assert.assertEquals(0, clientDataMap.size()); } @Test @@ -631,39 +629,36 @@ public class TokenServiceResourceTest { private RSAPublicKey publicKey; private RSAPrivateKey privateKey; - public TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) { + TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) { this.publicKey = publicKey; this.privateKey = privateKey; } @Override - public JWT issueToken(Subject subject, String algorithm) - throws TokenServiceException { + public JWT issueToken(Subject subject, String algorithm) { Principal p = (Principal) subject.getPrincipals().toArray()[0]; return issueToken(p, algorithm); } @Override - public JWT issueToken(Principal p, String algorithm) - throws TokenServiceException { + public JWT issueToken(Principal p, String algorithm) { return issueToken(p, null, algorithm); } @Override - public JWT issueToken(Principal p, String audience, String algorithm) - throws TokenServiceException { + public JWT issueToken(Principal p, String audience, String algorithm) { return issueToken(p, audience, algorithm, -1); } @Override - public boolean verifyToken(JWT token) throws TokenServiceException { + public boolean verifyToken(JWT token) { JWSVerifier verifier = new RSASSAVerifier(publicKey); return token.verify(verifier); } @Override public JWT issueToken(Principal p, String audience, String algorithm, - long expires) throws TokenServiceException { + long expires) { ArrayList<String> audiences = null; if (audience != null) { audiences = new ArrayList<>(); @@ -673,8 +668,13 @@ public class TokenServiceResourceTest { } @Override - public JWT issueToken(Principal p, List<String> audiences, String algorithm, - long expires) throws TokenServiceException { + public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires, + String signingkeyName, String signingkeyAlias, char[] signingkeyPassphrase) { + return issueToken(p, audiences, algorithm, expires); + } + + @Override + public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires) { String[] claimArray = new String[4]; claimArray[0] = "KNOXSSO"; claimArray[1] = p.getName(); @@ -693,18 +693,14 @@ public class TokenServiceResourceTest { } @Override - public JWT issueToken(Principal p, String algorithm, long expiry) - throws TokenServiceException { - return issueToken(p, Collections.<String>emptyList(), algorithm, expiry); + public JWT issueToken(Principal p, String algorithm, long expiry) { + return issueToken(p, Collections.emptyList(), algorithm, expiry); } @Override - public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException { + public boolean verifyToken(JWT token, RSAPublicKey publicKey) { JWSVerifier verifier = new RSASSAVerifier(publicKey); return token.verify(verifier); } - } - - } http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java index 30dfe31..0467565 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java @@ -22,33 +22,37 @@ import java.security.KeyStore; public interface KeystoreService { - public void createKeystoreForGateway() throws KeystoreServiceException; + void createKeystoreForGateway() throws KeystoreServiceException; - public void addSelfSignedCertForGateway(String alias, char[] passphrase) throws KeystoreServiceException; + void addSelfSignedCertForGateway(String alias, char[] passphrase) throws KeystoreServiceException; void addSelfSignedCertForGateway(String alias, char[] passphrase, String hostname) throws KeystoreServiceException; - public KeyStore getKeystoreForGateway() throws KeystoreServiceException; + KeyStore getKeystoreForGateway() throws KeystoreServiceException; - public KeyStore getSigningKeystore() throws KeystoreServiceException; + KeyStore getSigningKeystore() throws KeystoreServiceException; - public Key getKeyForGateway(String alias, char[] passphrase) throws KeystoreServiceException; + KeyStore getSigningKeystore(String keystoreName) throws KeystoreServiceException; - public Key getSigningKey(String alias, char[] passphrase) throws KeystoreServiceException; + Key getKeyForGateway(String alias, char[] passphrase) throws KeystoreServiceException; - public void createCredentialStoreForCluster(String clusterName) throws KeystoreServiceException; - - public boolean isCredentialStoreForClusterAvailable(String clusterName) throws KeystoreServiceException; + Key getSigningKey(String alias, char[] passphrase) throws KeystoreServiceException; - public boolean isKeystoreForGatewayAvailable() throws KeystoreServiceException; - - public KeyStore getCredentialStoreForCluster(String clusterName) throws KeystoreServiceException; + Key getSigningKey(String keystoreName, String alias, char[] passphrase) throws KeystoreServiceException; + + void createCredentialStoreForCluster(String clusterName) throws KeystoreServiceException; + + boolean isCredentialStoreForClusterAvailable(String clusterName) throws KeystoreServiceException; + + boolean isKeystoreForGatewayAvailable() throws KeystoreServiceException; + + KeyStore getCredentialStoreForCluster(String clusterName) throws KeystoreServiceException; - public void addCredentialForCluster(String clusterName, String alias, String key) throws KeystoreServiceException; + void addCredentialForCluster(String clusterName, String alias, String key) throws KeystoreServiceException; - public void removeCredentialForCluster(String clusterName, String alias) throws KeystoreServiceException; + void removeCredentialForCluster(String clusterName, String alias) throws KeystoreServiceException; - public char[] getCredentialForCluster(String clusterName, String alias) throws KeystoreServiceException; + char[] getCredentialForCluster(String clusterName, String alias) throws KeystoreServiceException; - public String getKeystorePath(); + String getKeystorePath(); } http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java index 6a5949b..2f71c2b 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java @@ -46,6 +46,10 @@ public interface JWTokenAuthority { JWT issueToken(Principal p, String audience, String algorithm, long expires) throws TokenServiceException; - JWT issueToken(Principal p, List<String> audience, String algorithm, + JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires) throws TokenServiceException; + + JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires, + String signingKeystoreName, String signingKeystoreAlias, char[] signingKeystorePassphrase) + throws TokenServiceException; } \ No newline at end of file
