You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by mo...@apache.org on 2017/10/25 19:20:38 UTC
[02/17] knox git commit: KNOX-1067 - Support different signature
algorithms for JWTs
KNOX-1067 - Support different signature algorithms for JWTs
Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/9c7aa7e1
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/9c7aa7e1
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/9c7aa7e1
Branch: refs/heads/KNOX-998-Package_Restructuring
Commit: 9c7aa7e1c7471f71c783681b68beea8e6f3fc2dc
Parents: 6acfa43
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Mon Oct 16 12:26:28 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Mon Oct 16 15:41:08 2017 +0100
----------------------------------------------------------------------
.../jwt/filter/AbstractJWTFilter.java | 45 ++++++--
.../jwt/filter/JWTFederationFilter.java | 5 +-
.../jwt/filter/SSOCookieFederationFilter.java | 5 +-
.../federation/AbstractJWTFilterTest.java | 102 ++++++++++++++++---
.../federation/SSOCookieProviderTest.java | 5 +-
.../impl/DefaultTokenAuthorityService.java | 22 +++-
.../impl/DefaultTokenAuthorityServiceTest.java | 93 +++++++++++++++++
.../gateway/service/knoxsso/WebSSOResource.java | 11 +-
.../service/knoxsso/WebSSOResourceTest.java | 69 +++++++++++--
.../service/knoxtoken/TokenResource.java | 11 +-
.../knoxtoken/TokenServiceResourceTest.java | 76 +++++++++++---
.../services/security/token/impl/JWTToken.java | 3 -
.../security/token/impl/JWTTokenTest.java | 45 +++++---
13 files changed, 412 insertions(+), 80 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
index 7f8e733..deb3d5b 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
@@ -22,6 +22,7 @@ import java.security.Principal;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
@@ -54,7 +55,9 @@ import org.apache.hadoop.gateway.security.PrimaryPrincipal;
import org.apache.hadoop.gateway.services.GatewayServices;
import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
import org.apache.hadoop.gateway.services.security.token.TokenServiceException;
-import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
+import org.apache.hadoop.gateway.services.security.token.impl.JWT;
+
+import com.nimbusds.jose.JWSHeader;
/**
*
@@ -67,6 +70,13 @@ public abstract class AbstractJWTFilter implements Filter {
public static final String JWT_EXPECTED_ISSUER = "jwt.expected.issuer";
public static final String JWT_DEFAULT_ISSUER = "KNOXSSO";
+ /**
+ * If specified, this configuration property refers to the signature algorithm which a received
+ * token must match. Otherwise, the default value "RS256" is used
+ */
+ public static final String JWT_EXPECTED_SIGALG = "jwt.expected.sigalg";
+ public static final String JWT_DEFAULT_SIGALG = "RS256";
+
static JWTMessages log = MessagesFactory.get( JWTMessages.class );
private static AuditService auditService = AuditServiceFactory.getAuditService();
private static Auditor auditor = auditService.getAuditor(
@@ -77,6 +87,7 @@ public abstract class AbstractJWTFilter implements Filter {
protected JWTokenAuthority authority;
protected RSAPublicKey publicKey = null;
private String expectedIssuer;
+ private String expectedSigAlg;
public abstract void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
@@ -99,11 +110,16 @@ public abstract class AbstractJWTFilter implements Filter {
}
}
- protected void configureExpectedIssuer(FilterConfig filterConfig) {
- expectedIssuer = filterConfig.getInitParameter(JWT_EXPECTED_ISSUER);;
+ protected void configureExpectedParameters(FilterConfig filterConfig) {
+ expectedIssuer = filterConfig.getInitParameter(JWT_EXPECTED_ISSUER);
if (expectedIssuer == null) {
expectedIssuer = JWT_DEFAULT_ISSUER;
}
+
+ expectedSigAlg = filterConfig.getInitParameter(JWT_EXPECTED_SIGALG);
+ if (expectedSigAlg == null) {
+ expectedSigAlg = JWT_DEFAULT_SIGALG;
+ }
}
/**
@@ -111,7 +127,7 @@ public abstract class AbstractJWTFilter implements Filter {
* @return
*/
protected List<String> parseExpectedAudiences(String expectedAudiences) {
- ArrayList<String> audList = null;
+ List<String> audList = null;
// setup the list of valid audiences for token validation
if (expectedAudiences != null) {
// parse into the list
@@ -124,7 +140,7 @@ public abstract class AbstractJWTFilter implements Filter {
return audList;
}
- protected boolean tokenIsStillValid(JWTToken jwtToken) {
+ protected boolean tokenIsStillValid(JWT jwtToken) {
// if there is no expiration date then the lifecycle is tied entirely to
// the cookie validity - otherwise ensure that the current time is before
// the designated expiration time
@@ -141,7 +157,7 @@ public abstract class AbstractJWTFilter implements Filter {
* the JWT token where the allowed audiences will be found
* @return true if an expected audience is present, otherwise false
*/
- protected boolean validateAudiences(JWTToken jwtToken) {
+ protected boolean validateAudiences(JWT jwtToken) {
boolean valid = false;
String[] tokenAudienceList = jwtToken.getAudienceClaims();
@@ -202,7 +218,7 @@ public abstract class AbstractJWTFilter implements Filter {
}
}
- protected Subject createSubjectFromToken(JWTToken token) {
+ protected Subject createSubjectFromToken(JWT token) {
final String principal = token.getSubject();
@SuppressWarnings("rawtypes")
@@ -223,7 +239,7 @@ public abstract class AbstractJWTFilter implements Filter {
}
protected boolean validateToken(HttpServletRequest request, HttpServletResponse response,
- FilterChain chain, JWTToken token)
+ FilterChain chain, JWT token)
throws IOException, ServletException {
boolean verified = false;
try {
@@ -237,6 +253,19 @@ public abstract class AbstractJWTFilter implements Filter {
log.unableToVerifyToken(e);
}
+ // Check received signature algorithm
+ if (verified) {
+ try {
+ String receivedSigAlg = JWSHeader.parse(token.getHeader()).getAlgorithm().getName();
+ if (!receivedSigAlg.equals(expectedSigAlg)) {
+ verified = false;
+ }
+ } catch (ParseException e) {
+ log.unableToVerifyToken(e);
+ verified = false;
+ }
+ }
+
if (verified) {
// confirm that issue matches intended target
if (expectedIssuer.equals(token.getIssuer())) {
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
index 401e449..dcc52c0 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
@@ -17,6 +17,7 @@
*/
package org.apache.hadoop.gateway.provider.federation.jwt.filter;
+import org.apache.hadoop.gateway.services.security.token.impl.JWT;
import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
import org.apache.hadoop.gateway.util.CertificateUtils;
@@ -63,7 +64,7 @@ public class JWTFederationFilter extends AbstractJWTFilter {
publicKey = CertificateUtils.parseRSAPublicKey(verificationPEM);
}
- configureExpectedIssuer(filterConfig);
+ configureExpectedParameters(filterConfig);
}
public void destroy() {
@@ -84,7 +85,7 @@ public class JWTFederationFilter extends AbstractJWTFilter {
if (wireToken != null) {
try {
- JWTToken token = new JWTToken(wireToken);
+ JWT token = new JWTToken(wireToken);
if (validateToken((HttpServletRequest)request, (HttpServletResponse)response, chain, token)) {
Subject subject = createSubjectFromToken(token);
continueWithEstablishedSecurityContext(subject, (HttpServletRequest)request, (HttpServletResponse)response, chain);
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
index cf14863..7e1c64a 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
@@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
import org.apache.hadoop.gateway.provider.federation.jwt.JWTMessages;
import org.apache.hadoop.gateway.security.PrimaryPrincipal;
+import org.apache.hadoop.gateway.services.security.token.impl.JWT;
import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
import org.apache.hadoop.gateway.util.CertificateUtils;
@@ -78,7 +79,7 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter {
publicKey = CertificateUtils.parseRSAPublicKey(verificationPEM);
}
- configureExpectedIssuer(filterConfig);
+ configureExpectedParameters(filterConfig);
}
public void destroy() {
@@ -105,7 +106,7 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter {
}
else {
try {
- JWTToken token = new JWTToken(wireToken);
+ JWT token = new JWTToken(wireToken);
if (validateToken((HttpServletRequest)request, (HttpServletResponse)response, chain, token)) {
Subject subject = createSubjectFromToken(token);
continueWithEstablishedSecurityContext(subject, (HttpServletRequest)request, (HttpServletResponse)response, chain);
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java
index bd34c04..b261081 100644
--- a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java
@@ -116,7 +116,8 @@ public abstract class AbstractJWTFilterTest {
Properties props = getProperties();
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+ new Date(new Date().getTime() + 5000), privateKey);
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setTokenOnRequest(request, jwt);
@@ -147,7 +148,8 @@ public abstract class AbstractJWTFilterTest {
props.put(getAudienceProperty(), "bar");
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+ new Date(new Date().getTime() + 5000), privateKey);
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setTokenOnRequest(request, jwt);
@@ -180,7 +182,8 @@ public abstract class AbstractJWTFilterTest {
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+ new Date(new Date().getTime() + 5000), privateKey);
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setTokenOnRequest(request, jwt);
@@ -209,7 +212,8 @@ public abstract class AbstractJWTFilterTest {
props.put(getAudienceProperty(), " foo, bar ");
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+ new Date(new Date().getTime() + 5000), privateKey);
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setTokenOnRequest(request, jwt);
@@ -245,7 +249,8 @@ public abstract class AbstractJWTFilterTest {
props.put(getVerificationPemProperty(), pem);
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 50000), privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+ new Date(new Date().getTime() + 50000), privateKey);
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setTokenOnRequest(request, jwt);
@@ -275,7 +280,8 @@ public abstract class AbstractJWTFilterTest {
Properties props = getProperties();
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() - 1000), privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+ new Date(new Date().getTime() - 1000), privateKey);
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setTokenOnRequest(request, jwt);
@@ -303,7 +309,7 @@ public abstract class AbstractJWTFilterTest {
Properties props = getProperties();
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("alice", null, privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", null, privateKey);
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setTokenOnRequest(request, jwt);
@@ -333,7 +339,8 @@ public abstract class AbstractJWTFilterTest {
Properties props = getProperties();
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("bob", new Date(new Date().getTime() + 5000), privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "bob",
+ new Date(new Date().getTime() + 5000), privateKey);
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setGarbledTokenOnRequest(request, jwt);
@@ -367,8 +374,8 @@ public abstract class AbstractJWTFilterTest {
Properties props = getProperties();
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("bob", new Date(new Date().getTime() + 5000),
- (RSAPrivateKey)kp.getPrivate(), props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "bob",
+ new Date(new Date().getTime() + 5000), (RSAPrivateKey)kp.getPrivate());
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setTokenOnRequest(request, jwt);
@@ -409,7 +416,8 @@ public abstract class AbstractJWTFilterTest {
props.put(getVerificationPemProperty(), failingPem);
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 50000), privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+ new Date(new Date().getTime() + 50000), privateKey);
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
setTokenOnRequest(request, jwt);
@@ -489,6 +497,67 @@ public abstract class AbstractJWTFilterTest {
}
}
+ @Test
+ public void testRS512SignatureAlgorithm() throws Exception {
+ try {
+ Properties props = getProperties();
+ props.put(AbstractJWTFilter.JWT_EXPECTED_SIGALG, "RS512");
+ handler.init(new TestFilterConfig(props));
+
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", new Date(new Date().getTime() + 5000),
+ privateKey, JWSAlgorithm.RS512.getName());
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ setTokenOnRequest(request, jwt);
+
+ EasyMock.expect(request.getRequestURL()).andReturn(
+ new StringBuffer(SERVICE_URL)).anyTimes();
+ EasyMock.expect(request.getQueryString()).andReturn(null);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
+ SERVICE_URL);
+ EasyMock.replay(request);
+
+ TestFilterChain chain = new TestFilterChain();
+ handler.doFilter(request, response, chain);
+ Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
+ Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
+ Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+ 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 testInvalidSignatureAlgorithm() throws Exception {
+ try {
+ Properties props = getProperties();
+ handler.init(new TestFilterConfig(props));
+
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", new Date(new Date().getTime() + 5000),
+ privateKey, JWSAlgorithm.RS384.getName());
+
+ 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 );
+ 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(
@@ -497,12 +566,13 @@ public abstract class AbstractJWTFilterTest {
return props;
}
- protected SignedJWT getJWT(String sub, Date expires, RSAPrivateKey privateKey,
- Properties props) throws Exception {
- return getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, sub, expires, privateKey);
+ protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey)
+ throws Exception {
+ return getJWT(issuer, sub, expires, privateKey, JWSAlgorithm.RS256.getName());
}
- protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey)
+ protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey,
+ String signatureAlgorithm)
throws Exception {
List<String> aud = new ArrayList<String>();
aud.add("bar");
@@ -515,7 +585,7 @@ public abstract class AbstractJWTFilterTest {
.claim("scope", "openid")
.build();
- JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).build();
+ JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.parse(signatureAlgorithm)).build();
SignedJWT signedJWT = new SignedJWT(header, claims);
JWSSigner signer = new RSASSASigner(privateKey);
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/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
index 768755b..38e7381 100644
--- 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
@@ -30,6 +30,7 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.hadoop.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
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;
@@ -70,8 +71,8 @@ public class SSOCookieProviderTest extends AbstractJWTFilterTest {
props.put("sso.cookie.name", "jowt");
handler.init(new TestFilterConfig(props));
- SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000),
- privateKey, props);
+ SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+ new Date(new Date().getTime() + 5000), privateKey);
Cookie cookie = new Cookie("jowt", jwt.serialize());
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
index 33b86bd..0c33cdf 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
@@ -23,8 +23,10 @@ import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Map;
+import java.util.Set;
import java.util.List;
import java.util.ArrayList;
+import java.util.HashSet;
import javax.security.auth.Subject;
@@ -48,10 +50,22 @@ import com.nimbusds.jose.crypto.RSASSAVerifier;
public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
private static final String SIGNING_KEY_PASSPHRASE = "signing.key.passphrase";
+ private static final Set<String> SUPPORTED_SIG_ALGS = new HashSet<>();
private AliasService as = null;
private KeystoreService ks = null;
String signingKeyAlias = null;
+ static {
+ // Only standard RSA signature algorithms are accepted
+ // https://tools.ietf.org/html/rfc7518
+ SUPPORTED_SIG_ALGS.add("RS256");
+ SUPPORTED_SIG_ALGS.add("RS384");
+ SUPPORTED_SIG_ALGS.add("RS512");
+ SUPPORTED_SIG_ALGS.add("PS256");
+ SUPPORTED_SIG_ALGS.add("PS384");
+ SUPPORTED_SIG_ALGS.add("PS512");
+ }
+
public void setKeystoreService(KeystoreService ks) {
this.ks = ks;
}
@@ -96,7 +110,7 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
@Override
public JWT issueToken(Principal p, String audience, String algorithm, long expires)
throws TokenServiceException {
- ArrayList<String> audiences = null;
+ List<String> audiences = null;
if (audience != null) {
audiences = new ArrayList<String>();
audiences.add(audience);
@@ -118,9 +132,9 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
claimArray[3] = String.valueOf(expires);
}
- JWTToken token = null;
- if ("RS256".equals(algorithm)) {
- token = new JWTToken("RS256", claimArray, audiences);
+ JWT token = null;
+ if (SUPPORTED_SIG_ALGS.contains(algorithm)) {
+ token = new JWTToken(algorithm, claimArray, audiences);
RSAPrivateKey key;
char[] passphrase = null;
try {
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java b/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
index 7cc9971..48616c0 100644
--- a/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
+++ b/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
@@ -27,6 +27,7 @@ import org.apache.hadoop.gateway.services.security.KeystoreService;
import org.apache.hadoop.gateway.services.security.MasterService;
import org.apache.hadoop.gateway.services.security.impl.DefaultKeystoreService;
import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
+import org.apache.hadoop.gateway.services.security.token.TokenServiceException;
import org.apache.hadoop.gateway.services.security.token.impl.JWT;
import org.easymock.EasyMock;
import org.junit.Test;
@@ -74,6 +75,8 @@ public class DefaultTokenAuthorityServiceTest extends org.junit.Assert {
JWT token = ta.issueToken(principal, "RS256");
assertEquals("KNOXSSO", token.getIssuer());
assertEquals("john.doe@example.com", token.getSubject());
+
+ assertTrue(ta.verifyToken(token));
}
@Test
@@ -115,6 +118,8 @@ public class DefaultTokenAuthorityServiceTest extends org.junit.Assert {
assertEquals("KNOXSSO", token.getIssuer());
assertEquals("john.doe@example.com", token.getSubject());
assertEquals("https://login.example.com", token.getAudience());
+
+ assertTrue(ta.verifyToken(token));
}
@Test
@@ -155,6 +160,94 @@ public class DefaultTokenAuthorityServiceTest extends org.junit.Assert {
JWT token = ta.issueToken(principal, null, "RS256");
assertEquals("KNOXSSO", token.getIssuer());
assertEquals("john.doe@example.com", token.getSubject());
+
+ assertTrue(ta.verifyToken(token));
+ }
+
+ @Test
+ public void testTokenCreationSignatureAlgorithm() throws Exception {
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("john.doe@example.com");
+
+ 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);
+
+ KeystoreService ks = new DefaultKeystoreService();
+ ((DefaultKeystoreService)ks).setMasterService(ms);
+
+ ((DefaultKeystoreService)ks).init(config, new HashMap<String, String>());
+
+ JWTokenAuthority ta = new DefaultTokenAuthorityService();
+ ((DefaultTokenAuthorityService)ta).setAliasService(as);
+ ((DefaultTokenAuthorityService)ta).setKeystoreService(ks);
+
+ ((DefaultTokenAuthorityService)ta).init(config, new HashMap<String, String>());
+
+ JWT token = ta.issueToken(principal, "RS512");
+ assertEquals("KNOXSSO", token.getIssuer());
+ assertEquals("john.doe@example.com", token.getSubject());
+ assertTrue(token.getHeader().contains("RS512"));
+
+ assertTrue(ta.verifyToken(token));
+ }
+
+ @Test
+ public void testTokenCreationBadSignatureAlgorithm() throws Exception {
+
+ Principal principal = EasyMock.createNiceMock(Principal.class);
+ EasyMock.expect(principal.getName()).andReturn("john.doe@example.com");
+
+ 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);
+
+ KeystoreService ks = new DefaultKeystoreService();
+ ((DefaultKeystoreService)ks).setMasterService(ms);
+
+ ((DefaultKeystoreService)ks).init(config, new HashMap<String, String>());
+
+ JWTokenAuthority ta = new DefaultTokenAuthorityService();
+ ((DefaultTokenAuthorityService)ta).setAliasService(as);
+ ((DefaultTokenAuthorityService)ta).setKeystoreService(ks);
+
+ ((DefaultTokenAuthorityService)ta).init(config, new HashMap<String, String>());
+
+ try {
+ ta.issueToken(principal, "none");
+ fail("Failure expected on a bad signature algorithm");
+ } catch (TokenServiceException ex) {
+ // expected
+ }
}
}
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
index 70228d3..36aa075 100644
--- a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
+++ b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
@@ -60,6 +60,7 @@ public class WebSSOResource {
private static final String SSO_COOKIE_DOMAIN_SUFFIX_PARAM = "knoxsso.cookie.domain.suffix";
private static final String SSO_COOKIE_TOKEN_TTL_PARAM = "knoxsso.token.ttl";
private static final String SSO_COOKIE_TOKEN_AUDIENCES_PARAM = "knoxsso.token.audiences";
+ 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_ENABLE_SESSION_PARAM = "knoxsso.enable.session";
private static final String ORIGINAL_URL_REQUEST_PARAM = "originalUrl";
@@ -77,6 +78,7 @@ public class WebSSOResource {
private String domainSuffix = null;
private List<String> targetAudiences = new ArrayList<>();
private boolean enableSession = false;
+ private String signatureAlgorithm = "RS256";
@Context
HttpServletRequest request;
@@ -143,6 +145,11 @@ public class WebSSOResource {
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;
+ }
}
@GET
@@ -185,9 +192,9 @@ public class WebSSOResource {
try {
JWT token = null;
if (targetAudiences.isEmpty()) {
- token = ts.issueToken(p, "RS256", getExpiry());
+ token = ts.issueToken(p, signatureAlgorithm, getExpiry());
} else {
- token = ts.issueToken(p, targetAudiences, "RS256", getExpiry());
+ token = ts.issueToken(p, targetAudiences, signatureAlgorithm, getExpiry());
}
// Coverity CID 1327959
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java b/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
index 568f0fe..516f9ae 100644
--- a/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
+++ b/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
@@ -166,7 +166,7 @@ public class WebSSOResourceTest {
Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
assertNotNull(cookie);
- JWTToken parsedToken = new JWTToken(cookie.getValue());
+ JWT parsedToken = new JWTToken(cookie.getValue());
assertEquals("alice", parsedToken.getSubject());
assertTrue(authority.verifyToken(parsedToken));
}
@@ -218,7 +218,7 @@ public class WebSSOResourceTest {
Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
assertNotNull(cookie);
- JWTToken parsedToken = new JWTToken(cookie.getValue());
+ JWT parsedToken = new JWTToken(cookie.getValue());
assertEquals("alice", parsedToken.getSubject());
assertTrue(authority.verifyToken(parsedToken));
@@ -287,6 +287,60 @@ public class WebSSOResourceTest {
assertTrue(audiences.contains("recipient2"));
}
+ @Test
+ public void testSignatureAlgorithm() throws Exception {
+
+ 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.token.sigalg")).andReturn("RS512");
+
+ 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.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);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
+ CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
+
+ EasyMock.replay(principal, services, context, request);
+
+ 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());
+ assertTrue(authority.verifyToken(parsedToken));
+ assertTrue(parsedToken.getHeader().contains("RS512"));
+ }
+
/**
* A wrapper for HttpServletResponseWrapper to store the cookies
*/
@@ -380,14 +434,9 @@ public class WebSSOResourceTest {
claimArray[3] = String.valueOf(expires);
}
- JWTToken token = null;
- if ("RS256".equals(algorithm)) {
- token = new JWTToken("RS256", claimArray, audiences);
- JWSSigner signer = new RSASSASigner(privateKey);
- token.sign(signer);
- } else {
- throw new TokenServiceException("Cannot issue token - Unsupported algorithm");
- }
+ JWT token = new JWTToken(algorithm, claimArray, audiences);
+ JWSSigner signer = new RSASSASigner(privateKey);
+ token.sign(signer);
return token;
}
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
index df8288a..afa6a3a 100644
--- a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
@@ -57,6 +57,7 @@ public class TokenResource {
private static final String TOKEN_CLIENT_DATA = "knox.token.client.data";
private static final String TOKEN_CLIENT_CERT_REQUIRED = "knox.token.client.cert.required";
private static final String TOKEN_ALLOWED_PRINCIPALS = "knox.token.allowed.principals";
+ private static final String TOKEN_SIG_ALG = "knox.token.sigalg";
static final String RESOURCE_PATH = "knoxtoken/api/v1/token";
private static TokenServiceMessages log = MessagesFactory.get( TokenServiceMessages.class );
private long tokenTTL = 30000l;
@@ -65,6 +66,7 @@ public class TokenResource {
private Map<String,Object> tokenClientDataMap = null;
private ArrayList<String> allowedDNs = new ArrayList<>();
private boolean clientCertRequired = false;
+ private String signatureAlgorithm = "RS256";
@Context
HttpServletRequest request;
@@ -115,6 +117,11 @@ public class TokenResource {
String[] tokenClientData = clientData.split(",");
addClientDataToMap(tokenClientData, tokenClientDataMap);
}
+
+ String sigAlg = context.getInitParameter(TOKEN_SIG_ALG);
+ if (sigAlg != null) {
+ signatureAlgorithm = sigAlg;
+ }
}
@GET
@@ -159,9 +166,9 @@ public class TokenResource {
try {
JWT token = null;
if (targetAudiences.isEmpty()) {
- token = ts.issueToken(p, "RS256", expires);
+ token = ts.issueToken(p, signatureAlgorithm, expires);
} else {
- token = ts.issueToken(p, targetAudiences, "RS256", expires);
+ token = ts.issueToken(p, targetAudiences, signatureAlgorithm, expires);
}
if (token != null) {
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
index 0046bd9..80f359d 100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -101,11 +101,8 @@ public class TokenServiceResourceTest {
@Test
public void testGetToken() throws Exception {
- TokenResource tr = new TokenResource();
ServletContext context = EasyMock.createNiceMock(ServletContext.class);
- //tr.context = context;
- // tr.init();
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
@@ -126,6 +123,7 @@ public class TokenServiceResourceTest {
EasyMock.replay(principal, services, context, request, response);
+ TokenResource tr = new TokenResource();
tr.request = request;
tr.response = response;
@@ -142,7 +140,7 @@ public class TokenServiceResourceTest {
assertNotNull(expiry);
// Verify the token
- JWTToken parsedToken = new JWTToken(accessToken);
+ JWT parsedToken = new JWTToken(accessToken);
assertEquals("alice", parsedToken.getSubject());
assertTrue(authority.verifyToken(parsedToken));
}
@@ -194,7 +192,7 @@ public class TokenServiceResourceTest {
assertNotNull(expiry);
// Verify the token
- JWTToken parsedToken = new JWTToken(accessToken);
+ JWT parsedToken = new JWTToken(accessToken);
assertEquals("alice", parsedToken.getSubject());
assertTrue(authority.verifyToken(parsedToken));
@@ -252,7 +250,7 @@ public class TokenServiceResourceTest {
assertNotNull(expiry);
// Verify the token
- JWTToken parsedToken = new JWTToken(accessToken);
+ JWT parsedToken = new JWTToken(accessToken);
assertEquals("alice", parsedToken.getSubject());
assertTrue(authority.verifyToken(parsedToken));
@@ -315,7 +313,7 @@ public class TokenServiceResourceTest {
assertNotNull(expiry);
// Verify the token
- JWTToken parsedToken = new JWTToken(accessToken);
+ JWT parsedToken = new JWTToken(accessToken);
assertEquals("alice", parsedToken.getSubject());
assertTrue(authority.verifyToken(parsedToken));
}
@@ -405,6 +403,59 @@ public class TokenServiceResourceTest {
assertEquals(403, retResponse.getStatus());
}
+ @Test
+ public void testSignatureAlgorithm() throws Exception {
+ ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+ EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
+ EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
+ EasyMock.expect(context.getInitParameter("knox.token.sigalg")).andReturn("RS512");
+
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ 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);
+
+ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+ EasyMock.replay(principal, services, context, request, response);
+
+ TokenResource tr = new TokenResource();
+ tr.request = request;
+ tr.response = response;
+ tr.context = context;
+ tr.init();
+
+ // Issue a token
+ Response retResponse = tr.doGet();
+
+ assertEquals(200, retResponse.getStatus());
+
+ // Parse the response
+ String retString = writer.toString();
+ String accessToken = getTagValue(retString, "access_token");
+ assertNotNull(accessToken);
+ String expiry = getTagValue(retString, "expires_in");
+ assertNotNull(expiry);
+
+ // Verify the token
+ JWT parsedToken = new JWTToken(accessToken);
+ assertEquals("alice", parsedToken.getSubject());
+ assertTrue(authority.verifyToken(parsedToken));
+ assertTrue(parsedToken.getHeader().contains("RS512"));
+ }
+
private String getTagValue(String token, String tagName) {
String searchString = tagName + "\":";
String value = token.substring(token.indexOf(searchString) + searchString.length());
@@ -479,14 +530,9 @@ public class TokenServiceResourceTest {
claimArray[3] = String.valueOf(expires);
}
- JWTToken token = null;
- if ("RS256".equals(algorithm)) {
- token = new JWTToken("RS256", claimArray, audiences);
- JWSSigner signer = new RSASSASigner(privateKey);
- token.sign(signer);
- } else {
- throw new TokenServiceException("Cannot issue token - Unsupported algorithm");
- }
+ JWT token = new JWTToken(algorithm, claimArray, audiences);
+ JWSSigner signer = new RSASSASigner(privateKey);
+ token.sign(signer);
return token;
}
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
index 567c156..be2a331 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
@@ -17,14 +17,11 @@
*/
package org.apache.hadoop.gateway.services.security.token.impl;
-import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.Date;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
-import org.apache.commons.codec.binary.Base64;
import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
import com.nimbusds.jose.JOSEException;
http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java b/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java
index 6372f0c..d971eca 100644
--- a/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java
+++ b/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java
@@ -22,9 +22,12 @@ import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
+import org.junit.BeforeClass;
import org.junit.Test;
import com.nimbusds.jose.JWSAlgorithm;
@@ -37,10 +40,11 @@ public class JWTTokenTest extends org.junit.Assert {
private static final String JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MTY5MjkxMDksImp0aSI6ImFhN2Y4ZDBhOTVjIiwic2NvcGVzIjpbInJlcG8iLCJwdWJsaWNfcmVwbyJdfQ.XCEwpBGvOLma4TCoh36FU7XhUbcskygS81HE1uHLf0E";
private static final String HEADER = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
- private RSAPublicKey publicKey;
- private RSAPrivateKey privateKey;
+ private static RSAPublicKey publicKey;
+ private static RSAPrivateKey privateKey;
- public JWTTokenTest() throws Exception, NoSuchAlgorithmException {
+ @BeforeClass
+ public static void setup() throws Exception, NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
@@ -64,7 +68,7 @@ public class JWTTokenTest extends org.junit.Assert {
claims[1] = "john.doe@example.com";
claims[2] = "https://login.example.com";
claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
- JWTToken token = new JWTToken("RS256", claims);
+ JWT token = new JWTToken("RS256", claims);
assertEquals("KNOXSSO", token.getIssuer());
assertEquals("john.doe@example.com", token.getSubject());
@@ -78,10 +82,10 @@ public class JWTTokenTest extends org.junit.Assert {
claims[1] = "john.doe@example.com";
claims[2] = null;
claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
- ArrayList<String> audiences = new ArrayList<String>();
+ List<String> audiences = new ArrayList<String>();
audiences.add("https://login.example.com");
- JWTToken token = new JWTToken("RS256", claims, audiences);
+ JWT token = new JWTToken("RS256", claims, audiences);
assertEquals("KNOXSSO", token.getIssuer());
assertEquals("john.doe@example.com", token.getSubject());
@@ -96,11 +100,11 @@ public class JWTTokenTest extends org.junit.Assert {
claims[1] = "john.doe@example.com";
claims[2] = null;
claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
- ArrayList<String> audiences = new ArrayList<String>();
+ List<String> audiences = new ArrayList<String>();
audiences.add("https://login.example.com");
audiences.add("KNOXSSO");
- JWTToken token = new JWTToken("RS256", claims, audiences);
+ JWT token = new JWTToken("RS256", claims, audiences);
assertEquals("KNOXSSO", token.getIssuer());
assertEquals("john.doe@example.com", token.getSubject());
@@ -134,9 +138,9 @@ public class JWTTokenTest extends org.junit.Assert {
claims[1] = "john.doe@example.com";
claims[2] = null;
claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
- ArrayList<String> audiences = null;
+ List<String> audiences = null;
- JWTToken token = new JWTToken("RS256", claims, audiences);
+ JWT token = new JWTToken("RS256", claims, audiences);
assertEquals("KNOXSSO", token.getIssuer());
assertEquals("john.doe@example.com", token.getSubject());
@@ -166,8 +170,7 @@ public class JWTTokenTest extends org.junit.Assert {
claims[1] = "john.doe@example.com";
claims[2] = "https://login.example.com";
claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
- JWTToken token = new JWTToken("RS256", claims);
-
+ JWT token = new JWTToken("RS256", claims);
assertEquals("KNOXSSO", token.getIssuer());
assertEquals("john.doe@example.com", token.getSubject());
@@ -190,7 +193,7 @@ public class JWTTokenTest extends org.junit.Assert {
claims[1] = "john.doe@example.com";
claims[2] = "https://login.example.com";
claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
- JWTToken token = new JWTToken(JWSAlgorithm.RS512.getName(), claims);
+ JWT token = new JWTToken(JWSAlgorithm.RS512.getName(), claims);
assertEquals("KNOXSSO", token.getIssuer());
assertEquals("john.doe@example.com", token.getSubject());
@@ -214,10 +217,24 @@ public class JWTTokenTest extends org.junit.Assert {
claims[1] = "john.doe@example.com";
claims[2] = "https://login.example.com";
claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
- JWTToken token = new JWTToken("RS256", claims);
+ JWT token = new JWTToken("RS256", claims);
assertNotNull(token.getExpires());
assertNotNull(token.getExpiresDate());
assertEquals(token.getExpiresDate(), new Date(Long.valueOf(token.getExpires())));
}
+
+ @Test
+ public void testUnsignedToken() throws Exception {
+ String unsignedToken = "eyJhbGciOiJub25lIn0.eyJzdWIiOiJhbGljZSIsImp0aSI6ImY2YmNj"
+ + "MDVjLWI4MTktNGM0Mi1iMGMyLWJlYmY1MDE4YWFiZiJ9.";
+
+ try {
+ new JWTToken(unsignedToken);
+ fail("Failure expected on an unsigned token");
+ } catch (ParseException ex) {
+ // expected
+ }
+ }
+
}