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:52 UTC

[16/17] knox git commit: Merge branch 'master' into KNOX-998-Package_Restructuring

Merge branch 'master' into KNOX-998-Package_Restructuring

# Conflicts:
#	gateway-provider-rewrite-func-hostmap-static/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ProviderDeploymentContributor
#	gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
#	gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
#	gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
#	gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
#	gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
#	gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
#	gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWTToken.java


Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/58780d37
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/58780d37
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/58780d37

Branch: refs/heads/KNOX-998-Package_Restructuring
Commit: 58780d37c96f1f49f3863ecd065a09fb3bfda26a
Parents: 7d0bff1 994ac32
Author: Sandeep More <mo...@apache.org>
Authored: Wed Oct 25 10:59:55 2017 -0400
Committer: Sandeep More <mo...@apache.org>
Committed: Wed Oct 25 10:59:55 2017 -0400

----------------------------------------------------------------------
 .../discovery/ambari/AmbariCluster.java         |   2 +-
 .../provider/impl/BaseZookeeperURLManager.java  | 195 ++++++++++++
 .../provider/impl/HBaseZookeeperURLManager.java | 138 +++++++++
 .../provider/impl/KafkaZookeeperURLManager.java | 152 ++++++++++
 .../provider/impl/SOLRZookeeperURLManager.java  | 118 ++++++++
 .../ha/provider/impl/StringResponseHandler.java |  49 +++
 .../impl/HBaseZookeeperURLManagerTest.java      |  72 +++++
 .../impl/KafkaZookeeperURLManagerTest.java      |  71 +++++
 .../impl/SOLRZookeeperURLManagerTest.java       | 110 +++++++
 ...gateway.deploy.ProviderDeploymentContributor |   5 +-
 .../provider/federation/jwt/JWTMessages.java    |   3 +
 .../jwt/filter/AbstractJWTFilter.java           |  57 +++-
 .../jwt/filter/JWTFederationFilter.java         |   5 +-
 .../jwt/filter/SSOCookieFederationFilter.java   |   5 +-
 .../federation/AbstractJWTFilterTest.java       | 239 +++++++++++++--
 .../federation/SSOCookieProviderTest.java       |   5 +-
 gateway-provider-security-pac4j/pom.xml         |  31 +-
 .../pac4j/filter/Pac4jDispatcherFilter.java     |  15 +-
 .../pac4j/filter/Pac4jIdentityAdapter.java      |  36 ++-
 .../gateway/pac4j/session/KnoxSessionStore.java |  28 +-
 .../knox/gateway/pac4j/Pac4jProviderTest.java   |  10 +-
 .../impl/DefaultTokenAuthorityService.java      |  22 +-
 .../topology/impl/DefaultTopologyService.java   |  16 +
 .../topology/simple/SimpleDescriptor.java       |   4 +-
 .../simple/SimpleDescriptorHandler.java         |  43 ++-
 .../topology/simple/SimpleDescriptorImpl.java   |  12 +
 .../impl/DefaultTokenAuthorityServiceTest.java  |  94 ++++++
 .../simple/SimpleDescriptorFactoryTest.java     | 230 +++++++++++++-
 .../simple/SimpleDescriptorHandlerTest.java     |  79 ++++-
 .../gateway/service/knoxsso/WebSSOResource.java |  18 +-
 .../service/knoxsso/WebSSOResourceTest.java     | 299 +++++++++++++++++-
 .../service/knoxtoken/TokenResource.java        |  20 +-
 .../knoxtoken/TokenServiceResourceTest.java     | 302 ++++++++++++++++++-
 .../apache/knox/gateway/shell/job/Sqoop.java    |   2 +-
 .../services/security/token/impl/JWT.java       |   3 +
 .../services/security/token/impl/JWTToken.java  |  38 ++-
 .../security/token/impl/JWTTokenTest.java       |  45 ++-
 pom.xml                                         |   2 +-
 38 files changed, 2409 insertions(+), 166 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariCluster.java
----------------------------------------------------------------------
diff --cc gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariCluster.java
index d65bff7,0000000..d71e079
mode 100644,000000..100644
--- a/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariCluster.java
+++ b/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariCluster.java
@@@ -1,115 -1,0 +1,115 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one or more
 + * contributor license agreements. See the NOTICE file distributed with this
 + * work for additional information regarding copyright ownership. The ASF
 + * licenses this file to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + * http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 + * License for the specific language governing permissions and limitations under
 + * the License.
 + */
 +package org.apache.knox.gateway.topology.discovery.ambari;
 +
 +import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
 +
 +import java.util.ArrayList;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +
 +class AmbariCluster implements ServiceDiscovery.Cluster {
 +
 +    private String name = null;
 +
 +    private AmbariDynamicServiceURLCreator urlCreator;
 +
 +    private Map<String, Map<String, ServiceConfiguration>> serviceConfigurations = new HashMap<>();
 +
 +    private Map<String, AmbariComponent> components = null;
 +
 +
 +    AmbariCluster(String name) {
 +        this.name = name;
 +        components = new HashMap<>();
 +        urlCreator = new AmbariDynamicServiceURLCreator(this);
 +    }
 +
 +    void addServiceConfiguration(String serviceName, String configurationType, ServiceConfiguration serviceConfig) {
 +        if (!serviceConfigurations.keySet().contains(serviceName)) {
-             serviceConfigurations.put(serviceName, new HashMap<String, ServiceConfiguration>());
++            serviceConfigurations.put(serviceName, new HashMap<>());
 +        }
 +        serviceConfigurations.get(serviceName).put(configurationType, serviceConfig);
 +    }
 +
 +
 +    void addComponent(AmbariComponent component) {
 +        components.put(component.getName(), component);
 +    }
 +
 +
 +    ServiceConfiguration getServiceConfiguration(String serviceName, String configurationType) {
 +        ServiceConfiguration sc = null;
 +        Map<String, ServiceConfiguration> configs = serviceConfigurations.get(serviceName);
 +        if (configs != null) {
 +            sc = configs.get(configurationType);
 +        }
 +        return sc;
 +    }
 +
 +
 +    Map<String, AmbariComponent> getComponents() {
 +        return components;
 +    }
 +
 +
 +    AmbariComponent getComponent(String name) {
 +        return components.get(name);
 +    }
 +
 +
 +    @Override
 +    public String getName() {
 +        return name;
 +    }
 +
 +
 +    @Override
 +    public List<String> getServiceURLs(String serviceName) {
 +        List<String> urls = new ArrayList<>();
 +        urls.addAll(urlCreator.create(serviceName));
 +        return urls;
 +    }
 +
 +
 +    static class ServiceConfiguration {
 +
 +        private String type;
 +        private String version;
 +        private Map<String, String> props;
 +
 +        ServiceConfiguration(String type, String version, Map<String, String> properties) {
 +            this.type = type;
 +            this.version = version;
 +            this.props = properties;
 +        }
 +
 +        public String getVersion() {
 +            return version;
 +        }
 +
 +        public String getType() {
 +            return type;
 +        }
 +
 +        public Map<String, String> getProperties() {
 +            return props;
 +        }
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-provider-rewrite-func-hostmap-static/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ProviderDeploymentContributor
----------------------------------------------------------------------
diff --cc gateway-provider-rewrite-func-hostmap-static/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ProviderDeploymentContributor
index 76328d9,0000000..d6b9608
mode 100644,000000..100644
--- a/gateway-provider-rewrite-func-hostmap-static/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ProviderDeploymentContributor
+++ b/gateway-provider-rewrite-func-hostmap-static/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ProviderDeploymentContributor
@@@ -1,19 -1,0 +1,22 @@@
 +##########################################################################
 +# Licensed to the Apache Software Foundation (ASF) under one
 +# or more contributor license agreements.  See the NOTICE file
 +# distributed with this work for additional information
 +# regarding copyright ownership.  The ASF licenses this file
 +# to you under the Apache License, Version 2.0 (the
 +# "License"); you may not use this file except in compliance
 +# with the License.  You may obtain a copy of the License at
 +#
 +#     http://www.apache.org/licenses/LICENSE-2.0
 +#
 +# Unless required by applicable law or agreed to in writing, software
 +# distributed under the License is distributed on an "AS IS" BASIS,
 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 +# See the License for the specific language governing permissions and
 +# limitations under the License.
 +##########################################################################
 +
- org.apache.knox.gateway.hostmap.impl.HostmapDeploymentContributor
++org.apache.knox.gateway.ha.provider.impl.HS2ZookeeperURLManager
++org.apache.knox.gateway.ha.provider.impl.SOLRZookeeperURLManager
++org.apache.knox.gateway.ha.provider.impl.KafkaZookeeperURLManager
++org.apache.knox.gateway.ha.provider.impl.HBaseZookeeperURLManager

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
index e1e0dcb,0000000..70efa8c
mode 100644,000000..100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
@@@ -1,57 -1,0 +1,60 @@@
 +/**
 +
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.provider.federation.jwt;
 +
 +import org.apache.knox.gateway.i18n.messages.Message;
 +import org.apache.knox.gateway.i18n.messages.MessageLevel;
 +import org.apache.knox.gateway.i18n.messages.Messages;
 +import org.apache.knox.gateway.i18n.messages.StackTrace;
 +
 +@Messages(logger="org.apache.knox.gateway.provider.federation.jwt")
 +public interface JWTMessages {
 +  @Message( level = MessageLevel.WARN, text = "Failed to validate the audience attribute." )
 +  void failedToValidateAudience();
 +
 +  @Message( level = MessageLevel.WARN, text = "Failed to verify the token signature." )
 +  void failedToVerifyTokenSignature();
 +
 +  @Message( level = MessageLevel.INFO, text = "Access token has expired; a new one must be acquired." )
 +  void tokenHasExpired();
 +
++  @Message( level = MessageLevel.INFO, text = "The NotBefore check failed." )
++  void notBeforeCheckFailed();
++
 +  @Message( level = MessageLevel.WARN, text = "Expected Bearer token is missing." )
 +  void missingBearerToken();
 +
 +  @Message( level = MessageLevel.INFO, text = "Unable to verify token: {0}" )
 +  void unableToVerifyToken(@StackTrace( level = MessageLevel.ERROR) Exception e);
 +
 +  @Message( level = MessageLevel.ERROR, text = "Unable to verify token: {0}" )
 +  void unableToIssueToken(@StackTrace( level = MessageLevel.DEBUG) Exception e);
 +
 +  @Message( level = MessageLevel.DEBUG, text = "Sending redirect to: {0}" )
 +  void sendRedirectToLoginURL(String loginURL);
 +
 +  @Message( level = MessageLevel.ERROR, text = "Required configuration element for authentication provider is missing." )
 +  void missingAuthenticationProviderUrlConfiguration();
 +
 +  @Message( level = MessageLevel.DEBUG, text = "{0} Cookie has been found and is being processed." )
 +  void cookieHasBeenFound(String cookieName);
 +
 +  @Message( level = MessageLevel.DEBUG, text = "Audience claim has been validated." )
 +  void jwtAudienceValidated();
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
index 077fa05,0000000..49357f0
mode 100644,000000..100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
@@@ -1,278 -1,0 +1,315 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.provider.federation.jwt.filter;
 +
 +import java.io.IOException;
 +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;
 +import java.util.List;
 +import java.util.Set;
 +
 +import javax.security.auth.Subject;
 +import javax.servlet.Filter;
 +import javax.servlet.FilterChain;
 +import javax.servlet.FilterConfig;
 +import javax.servlet.ServletContext;
 +import javax.servlet.ServletException;
 +import javax.servlet.ServletRequest;
 +import javax.servlet.ServletResponse;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +
 +import org.apache.knox.gateway.audit.api.Action;
 +import org.apache.knox.gateway.audit.api.ActionOutcome;
 +import org.apache.knox.gateway.audit.api.AuditContext;
 +import org.apache.knox.gateway.audit.api.AuditService;
 +import org.apache.knox.gateway.audit.api.AuditServiceFactory;
 +import org.apache.knox.gateway.audit.api.Auditor;
 +import org.apache.knox.gateway.audit.api.ResourceType;
 +import org.apache.knox.gateway.audit.log4j.audit.AuditConstants;
 +import org.apache.knox.gateway.filter.AbstractGatewayFilter;
 +import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 +import org.apache.knox.gateway.provider.federation.jwt.JWTMessages;
 +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.JWTToken;
++import org.apache.commons.lang.StringUtils;
++import org.apache.knox.gateway.services.security.token.impl.JWT;
++
++import com.nimbusds.jose.JWSHeader;
 +
 +/**
 + *
 + */
 +public abstract class AbstractJWTFilter implements Filter {
 +  /**
 +   * If specified, this configuration property refers to a value which the issuer of a received
 +   * token must match. Otherwise, the default value "KNOXSSO" is used
 +   */
 +  public static final String JWT_EXPECTED_ISSUER = "jwt.expected.issuer";
 +  public static final String JWT_DEFAULT_ISSUER = "KNOXSSO";
 +
++  /**
++   * 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(
 +      AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME,
 +      AuditConstants.KNOX_COMPONENT_NAME );
 +
 +  protected List<String> audiences;
 +  protected JWTokenAuthority authority;
 +  protected RSAPublicKey publicKey = null;
 +  private String expectedIssuer;
++  private String expectedSigAlg;
 +
 +  public abstract void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
 +      throws IOException, ServletException;
 +
 +  /**
 +   *
 +   */
 +  public AbstractJWTFilter() {
 +    super();
 +  }
 +
 +  @Override
 +  public void init( FilterConfig filterConfig ) throws ServletException {
 +    ServletContext context = filterConfig.getServletContext();
 +    if (context != null) {
 +      GatewayServices services = (GatewayServices) context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +      if (services != null) {
 +        authority = (JWTokenAuthority) services.getService(GatewayServices.TOKEN_SERVICE);
 +      }
 +    }
 +  }
 +
-   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;
++    }
 +  }
 +
 +  /**
 +   * @param expectedAudiences
 +   * @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) {
++    if (!StringUtils.isEmpty(expectedAudiences)) {
 +      // parse into the list
 +      String[] audArray = expectedAudiences.split(",");
 +      audList = new ArrayList<String>();
 +      for (String a : audArray) {
 +        audList.add(a.trim());
 +      }
 +    }
 +    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
 +    Date expires = jwtToken.getExpiresDate();
 +    return (expires == null || expires != null && new Date().before(expires));
 +  }
 +
 +  /**
 +   * Validate whether any of the accepted audience claims is present in the
 +   * issued token claims list for audience. Override this method in subclasses
 +   * in order to customize the audience validation behavior.
 +   *
 +   * @param jwtToken
 +   *          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();
 +    // if there were no expected audiences configured then just
 +    // consider any audience acceptable
 +    if (audiences == null) {
 +      valid = true;
 +    } else {
 +      // if any of the configured audiences is found then consider it
 +      // acceptable
 +      if (tokenAudienceList != null) {
 +        for (String aud : tokenAudienceList) {
 +          if (audiences.contains(aud)) {
 +            log.jwtAudienceValidated();
 +            valid = true;
 +            break;
 +          }
 +        }
 +      }
 +    }
 +    return valid;
 +  }
 +
 +  protected void continueWithEstablishedSecurityContext(Subject subject, final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
 +    Principal principal = (Principal) subject.getPrincipals(PrimaryPrincipal.class).toArray()[0];
 +    AuditContext context = auditService.getContext();
 +    if (context != null) {
 +      context.setUsername( principal.getName() );
 +      String sourceUri = (String)request.getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME );
 +      if (sourceUri != null) {
 +        auditor.audit( Action.AUTHENTICATION , sourceUri, ResourceType.URI, ActionOutcome.SUCCESS );
 +      }
 +    }
 +
 +    try {
 +      Subject.doAs(
 +        subject,
 +        new PrivilegedExceptionAction<Object>() {
 +          @Override
 +          public Object run() throws Exception {
 +            chain.doFilter(request, response);
 +            return null;
 +          }
 +        }
 +        );
 +    }
 +    catch (PrivilegedActionException e) {
 +      Throwable t = e.getCause();
 +      if (t instanceof IOException) {
 +        throw (IOException) t;
 +      }
 +      else if (t instanceof ServletException) {
 +        throw (ServletException) t;
 +      }
 +      else {
 +        throw new ServletException(t);
 +      }
 +    }
 +  }
 +
-   protected Subject createSubjectFromToken(JWTToken token) {
++  protected Subject createSubjectFromToken(JWT token) {
 +    final String principal = token.getSubject();
 +
 +    @SuppressWarnings("rawtypes")
 +    HashSet emptySet = new HashSet();
 +    Set<Principal> principals = new HashSet<>();
 +    Principal p = new PrimaryPrincipal(principal);
 +    principals.add(p);
 +
 +    // The newly constructed Sets check whether this Subject has been set read-only
 +    // before permitting subsequent modifications. The newly created Sets also prevent
 +    // illegal modifications by ensuring that callers have sufficient permissions.
 +    //
 +    // To modify the Principals Set, the caller must have AuthPermission("modifyPrincipals").
 +    // To modify the public credential Set, the caller must have AuthPermission("modifyPublicCredentials").
 +    // To modify the private credential Set, the caller must have AuthPermission("modifyPrivateCredentials").
 +    javax.security.auth.Subject subject = new javax.security.auth.Subject(true, principals, emptySet, emptySet);
 +    return subject;
 +  }
 +
 +  protected boolean validateToken(HttpServletRequest request, HttpServletResponse response,
-       FilterChain chain, JWTToken token)
++      FilterChain chain, JWT token)
 +      throws IOException, ServletException {
 +    boolean verified = false;
 +    try {
 +      if (publicKey == null) {
 +        verified = authority.verifyToken(token);
 +      }
 +      else {
 +        verified = authority.verifyToken(token, publicKey);
 +      }
 +    } catch (TokenServiceException e) {
 +      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())) {
 +        // if there is no expiration data then the lifecycle is tied entirely to
 +        // the cookie validity - otherwise ensure that the current time is before
 +        // the designated expiration time
 +        if (tokenIsStillValid(token)) {
 +          boolean audValid = validateAudiences(token);
 +          if (audValid) {
-             return true;
++              Date nbf = token.getNotBeforeDate();
++              if (nbf == null || new Date().after(nbf)) {
++                return true;
++              } else {
++                log.notBeforeCheckFailed();
++                handleValidationError(request, response, HttpServletResponse.SC_BAD_REQUEST,
++                                      "Bad request: the NotBefore check failed");
++              }
 +          }
 +          else {
 +            log.failedToValidateAudience();
 +            handleValidationError(request, response, HttpServletResponse.SC_BAD_REQUEST,
 +                                  "Bad request: missing required token audience");
 +          }
 +        }
 +        else {
 +          log.tokenHasExpired();
 +          handleValidationError(request, response, HttpServletResponse.SC_BAD_REQUEST,
 +                                "Bad request: token has expired");
 +        }
 +      }
 +      else {
 +        handleValidationError(request, response, HttpServletResponse.SC_UNAUTHORIZED, null);
 +      }
 +    }
 +    else {
 +      log.failedToVerifyTokenSignature();
 +      handleValidationError(request, response, HttpServletResponse.SC_UNAUTHORIZED, null);
 +    }
 +
 +    return false;
 +  }
 +
 +  protected abstract void handleValidationError(HttpServletRequest request, HttpServletResponse response, int status,
 +                                                String error) throws IOException;
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
index ec0e980,0000000..187d2b0
mode 100644,000000..100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
@@@ -1,111 -1,0 +1,112 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.provider.federation.jwt.filter;
 +
 +import org.apache.knox.gateway.services.security.token.impl.JWTToken;
 +import org.apache.knox.gateway.util.CertificateUtils;
++import org.apache.knox.gateway.services.security.token.impl.JWT;
 +
 +import javax.security.auth.Subject;
 +import javax.servlet.FilterChain;
 +import javax.servlet.FilterConfig;
 +import javax.servlet.ServletException;
 +import javax.servlet.ServletRequest;
 +import javax.servlet.ServletResponse;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +
 +import java.io.IOException;
 +import java.text.ParseException;
 +
 +public class JWTFederationFilter extends AbstractJWTFilter {
 +
 +  public static final String KNOX_TOKEN_AUDIENCES = "knox.token.audiences";
 +  public static final String TOKEN_VERIFICATION_PEM = "knox.token.verification.pem";
 +  private static final String KNOX_TOKEN_QUERY_PARAM_NAME = "knox.token.query.param.name";
 +  private static final String BEARER = "Bearer ";
 +  private String paramName = "knoxtoken";
 +
 +  @Override
 +  public void init( FilterConfig filterConfig ) throws ServletException {
 +      super.init(filterConfig);
 +
 +    // expected audiences or null
 +    String expectedAudiences = filterConfig.getInitParameter(KNOX_TOKEN_AUDIENCES);
 +    if (expectedAudiences != null) {
 +      audiences = parseExpectedAudiences(expectedAudiences);
 +    }
 +
 +    // query param name for finding the provided knoxtoken
 +    String queryParamName = filterConfig.getInitParameter(KNOX_TOKEN_QUERY_PARAM_NAME);
 +    if (queryParamName != null) {
 +      paramName = queryParamName;
 +    }
 +
 +    // token verification pem
 +    String verificationPEM = filterConfig.getInitParameter(TOKEN_VERIFICATION_PEM);
 +    // setup the public key of the token issuer for verification
 +    if (verificationPEM != null) {
 +      publicKey = CertificateUtils.parseRSAPublicKey(verificationPEM);
 +    }
 +
-     configureExpectedIssuer(filterConfig);
++    configureExpectedParameters(filterConfig);
 +  }
 +
 +  public void destroy() {
 +  }
 +
 +  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
 +      throws IOException, ServletException {
 +    String header = ((HttpServletRequest) request).getHeader("Authorization");
 +    String wireToken = null;
 +    if (header != null && header.startsWith(BEARER)) {
 +      // what follows the bearer designator should be the JWT token being used to request or as an access token
 +      wireToken = header.substring(BEARER.length());
 +    }
 +    else {
 +      // check for query param
 +      wireToken = ((HttpServletRequest) request).getParameter(paramName);
 +    }
 +
 +    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);
 +        }
 +      } catch (ParseException ex) {
 +        ((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
 +      }
 +    }
 +    else {
 +      // no token provided in header
 +      ((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
 +    }
 +  }
 +
 +  protected void handleValidationError(HttpServletRequest request, HttpServletResponse response, int status,
 +                                       String error) throws IOException {
 +    if (error != null) {
 +      response.sendError(status, error);
 +    }
 +    else {
 +      response.sendError(status);
 +    }
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
index 8a5f1ef,0000000..dbdb364
mode 100644,000000..100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
@@@ -1,170 -1,0 +1,171 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.provider.federation.jwt.filter;
 +
 +import java.io.IOException;
 +import java.text.ParseException;
 +
 +import javax.security.auth.Subject;
 +import javax.servlet.FilterChain;
 +import javax.servlet.FilterConfig;
 +import javax.servlet.ServletException;
 +import javax.servlet.ServletRequest;
 +import javax.servlet.ServletResponse;
 +import javax.servlet.http.Cookie;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +
 +import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 +import org.apache.knox.gateway.provider.federation.jwt.JWTMessages;
 +import org.apache.knox.gateway.security.PrimaryPrincipal;
 +import org.apache.knox.gateway.services.security.token.impl.JWTToken;
 +import org.apache.knox.gateway.util.CertificateUtils;
++import org.apache.knox.gateway.services.security.token.impl.JWT;
 +
 +public class SSOCookieFederationFilter extends AbstractJWTFilter {
 +  public static final String SSO_COOKIE_NAME = "sso.cookie.name";
 +  public static final String SSO_EXPECTED_AUDIENCES = "sso.expected.audiences";
 +  public static final String SSO_AUTHENTICATION_PROVIDER_URL = "sso.authentication.provider.url";
 +  public static final String SSO_VERIFICATION_PEM = "sso.token.verification.pem";
 +  private static JWTMessages log = MessagesFactory.get( JWTMessages.class );
 +  private static final String ORIGINAL_URL_QUERY_PARAM = "originalUrl=";
 +  private static final String DEFAULT_SSO_COOKIE_NAME = "hadoop-jwt";
 +
 +  private String cookieName;
 +  private String authenticationProviderUrl;
 +
 +  @Override
 +  public void init( FilterConfig filterConfig ) throws ServletException {
 +    super.init(filterConfig);
 +
 +    // configured cookieName
 +    cookieName = filterConfig.getInitParameter(SSO_COOKIE_NAME);
 +    if (cookieName == null) {
 +      cookieName = DEFAULT_SSO_COOKIE_NAME;
 +    }
 +
 +    // expected audiences or null
 +    String expectedAudiences = filterConfig.getInitParameter(SSO_EXPECTED_AUDIENCES);
 +    if (expectedAudiences != null) {
 +      audiences = parseExpectedAudiences(expectedAudiences);
 +    }
 +
 +    // url to SSO authentication provider
 +    authenticationProviderUrl = filterConfig.getInitParameter(SSO_AUTHENTICATION_PROVIDER_URL);
 +    if (authenticationProviderUrl == null) {
 +      log.missingAuthenticationProviderUrlConfiguration();
 +      throw new ServletException("Required authentication provider URL is missing.");
 +    }
 +
 +    // token verification pem
 +    String verificationPEM = filterConfig.getInitParameter(SSO_VERIFICATION_PEM);
 +    // setup the public key of the token issuer for verification
 +    if (verificationPEM != null) {
 +      publicKey = CertificateUtils.parseRSAPublicKey(verificationPEM);
 +    }
 +
-     configureExpectedIssuer(filterConfig);
++    configureExpectedParameters(filterConfig);
 +  }
 +
 +  public void destroy() {
 +  }
 +
 +  @Override
 +  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
 +      throws IOException, ServletException {
 +    String wireToken = null;
 +    HttpServletRequest req = (HttpServletRequest) request;
 +
 +    String loginURL = constructLoginURL(req);
 +    wireToken = getJWTFromCookie(req);
 +    if (wireToken == null) {
 +      if (req.getMethod().equals("OPTIONS")) {
 +        // CORS preflight requests to determine allowed origins and related config
 +        // must be able to continue without being redirected
 +        Subject sub = new Subject();
 +        sub.getPrincipals().add(new PrimaryPrincipal("anonymous"));
 +        continueWithEstablishedSecurityContext(sub, req, (HttpServletResponse) response, chain);
 +      }
 +      log.sendRedirectToLoginURL(loginURL);
 +      ((HttpServletResponse) response).sendRedirect(loginURL);
 +    }
 +    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);
 +        }
 +      } catch (ParseException ex) {
 +        ((HttpServletResponse) response).sendRedirect(loginURL);
 +      }
 +    }
 +  }
 +
 +  protected void handleValidationError(HttpServletRequest request, HttpServletResponse response, int status,
 +                                       String error) throws IOException {
 +    String loginURL = constructLoginURL(request);
 +    response.sendRedirect(loginURL);
 +  }
 +
 +  /**
 +   * Encapsulate the acquisition of the JWT token from HTTP cookies within the
 +   * request.
 +   *
 +   * @param req servlet request to get the JWT token from
 +   * @return serialized JWT token
 +   */
 +  protected String getJWTFromCookie(HttpServletRequest req) {
 +    String serializedJWT = null;
 +    Cookie[] cookies = req.getCookies();
 +    if (cookies != null) {
 +      for (Cookie cookie : cookies) {
 +        if (cookieName.equals(cookie.getName())) {
 +          log.cookieHasBeenFound(cookieName);
 +          serializedJWT = cookie.getValue();
 +          break;
 +        }
 +      }
 +    }
 +    return serializedJWT;
 +  }
 +
 +  /**
 +   * Create the URL to be used for authentication of the user in the absence of
 +   * a JWT token within the incoming request.
 +   *
 +   * @param request for getting the original request URL
 +   * @return url to use as login url for redirect
 +   */
 +  protected String constructLoginURL(HttpServletRequest request) {
 +    String delimiter = "?";
 +    if (authenticationProviderUrl.contains("?")) {
 +      delimiter = "&";
 +    }
 +    String loginURL = authenticationProviderUrl + delimiter
 +        + ORIGINAL_URL_QUERY_PARAM
 +        + request.getRequestURL().append(getOriginalQueryString(request));
 +    return loginURL;
 +  }
 +
 +  private String getOriginalQueryString(HttpServletRequest request) {
 +    String originalQueryString = request.getQueryString();
 +    return (originalQueryString == null) ? "" : "?" + originalQueryString;
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
index 9888eab,0000000..f79a743
mode 100644,000000..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
@@@ -1,667 -1,0 +1,870 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.provider.federation;
 +
 +import static org.junit.Assert.fail;
 +
 +import java.io.IOException;
 +import java.net.InetAddress;
 +import java.security.AccessController;
 +import java.security.KeyPair;
 +import java.security.KeyPairGenerator;
 +import java.security.NoSuchAlgorithmException;
 +import java.security.Principal;
 +import java.security.PublicKey;
 +import java.security.cert.Certificate;
 +import java.security.interfaces.RSAPrivateKey;
 +import java.security.interfaces.RSAPublicKey;
 +import java.text.MessageFormat;
 +import java.util.Enumeration;
 +import java.util.List;
 +import java.util.ArrayList;
 +import java.util.Properties;
 +import java.util.Date;
 +import java.util.Set;
 +
 +import javax.security.auth.Subject;
 +import javax.servlet.FilterChain;
 +import javax.servlet.FilterConfig;
 +import javax.servlet.ServletContext;
 +import javax.servlet.ServletException;
 +import javax.servlet.ServletRequest;
 +import javax.servlet.ServletResponse;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +
 +import org.apache.commons.codec.binary.Base64;
 +import org.apache.knox.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
 +import org.apache.knox.gateway.provider.federation.jwt.filter.SSOCookieFederationFilter;
 +import org.apache.knox.gateway.security.PrimaryPrincipal;
 +import org.apache.knox.gateway.services.security.impl.X509CertificateUtil;
 +import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 +import org.apache.knox.gateway.services.security.token.TokenServiceException;
 +import org.apache.knox.gateway.services.security.token.impl.JWT;
 +import org.apache.knox.gateway.services.security.token.impl.JWTToken;
 +import org.easymock.EasyMock;
 +import org.junit.After;
 +import org.junit.Assert;
 +import org.junit.BeforeClass;
 +import org.junit.Test;
 +
 +import com.nimbusds.jose.*;
 +import com.nimbusds.jwt.JWTClaimsSet;
 +import com.nimbusds.jwt.SignedJWT;
 +import com.nimbusds.jose.crypto.RSASSASigner;
 +import com.nimbusds.jose.crypto.RSASSAVerifier;
 +
 +public abstract class AbstractJWTFilterTest  {
 +  private static final String SERVICE_URL = "https://localhost:8888/resource";
 +  private static final String dnTemplate = "CN={0},OU=Test,O=Hadoop,L=Test,ST=Test,C=US";
 +
 +  protected AbstractJWTFilter handler = null;
 +  protected static RSAPublicKey publicKey = null;
 +  protected static RSAPrivateKey privateKey = null;
 +  protected static String pem = null;
 +
 +  protected abstract void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt);
 +  protected abstract void setGarbledTokenOnRequest(HttpServletRequest request, SignedJWT jwt);
 +  protected abstract String getAudienceProperty();
 +  protected abstract String getVerificationPemProperty();
 +
 +  private static String buildDistinguishedName(String hostname) {
 +    MessageFormat headerFormatter = new MessageFormat(dnTemplate);
 +    String[] paramArray = new String[1];
 +    paramArray[0] = hostname;
 +    String dn = headerFormatter.format(paramArray);
 +    return dn;
 +  }
 +
 +  @BeforeClass
 +  public static void generateKeys() throws Exception, NoSuchAlgorithmException {
 +    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
 +    kpg.initialize(2048);
 +    KeyPair KPair = kpg.generateKeyPair();
 +    String dn = buildDistinguishedName(InetAddress.getLocalHost().getHostName());
 +    Certificate cert = X509CertificateUtil.generateCertificate(dn, KPair, 365, "SHA1withRSA");
 +    byte[] data = cert.getEncoded();
 +    Base64 encoder = new Base64( 76, "\n".getBytes( "ASCII" ) );
 +    pem = new String(encoder.encodeToString( data ).getBytes( "ASCII" )).trim();
 +
 +    publicKey = (RSAPublicKey) KPair.getPublic();
 +    privateKey = (RSAPrivateKey) KPair.getPrivate();
 +  }
 +
 +  @After
 +  public void teardown() throws Exception {
 +    handler.destroy();
 +  }
 +
 +  @Test
 +  public void testValidJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      handler.init(new TestFilterConfig(props));
 +
-       SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
++      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
++                             new Date(new Date().getTime() + 5000), privateKey);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal", !principals.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 testValidAudienceJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      props.put(getAudienceProperty(), "bar");
 +      handler.init(new TestFilterConfig(props));
 +
-       SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
++      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
++                             new Date(new Date().getTime() + 5000), privateKey);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal", !principals.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 testInvalidAudienceJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      props.put(getAudienceProperty(), "foo");
 +      props.put("sso.authentication.provider.url", "https://localhost:8443/gateway/knoxsso/api/v1/websso");
 +
 +      handler.init(new TestFilterConfig(props));
 +
-       SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
++      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
++                             new Date(new Date().getTime() + 5000), privateKey);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testValidAudienceJWTWhitespace() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      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);
++
++      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 testNoTokenAudience() throws Exception {
++    try {
++      Properties props = getProperties();
++      props.put(getAudienceProperty(), "bar");
++      handler.init(new TestFilterConfig(props));
++
++      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", null,
++                             new Date(new Date().getTime() + 5000), new Date(), privateKey, "RS256");
++
++      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
++      setTokenOnRequest(request, jwt);
++
++      EasyMock.expect(request.getRequestURL()).andReturn(
++          new StringBuffer(SERVICE_URL)).anyTimes();
++      EasyMock.expect(request.getQueryString()).andReturn(null);
++      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
++      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
++          SERVICE_URL);
++      EasyMock.replay(request);
++
++      TestFilterChain chain = new TestFilterChain();
++      handler.doFilter(request, response, chain);
++      Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled);
++      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
++    } catch (ServletException se) {
++      fail("Should NOT have thrown a ServletException.");
++    }
++  }
++
++  @Test
++  public void testNoAudienceConfigured() throws Exception {
++    try {
++      Properties props = getProperties();
++      handler.init(new TestFilterConfig(props));
++
++      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", null,
++                             new Date(new Date().getTime() + 5000), new Date(), privateKey, "RS256");
++
++      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 testEmptyAudienceConfigured() throws Exception {
++    try {
++      Properties props = getProperties();
++      props.put(getAudienceProperty(), "");
++      handler.init(new TestFilterConfig(props));
++
++      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", null,
++                             new Date(new Date().getTime() + 5000), new Date(), privateKey, "RS256");
 +
 +      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 testValidVerificationPEM() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +
 +//      System.out.println("+" + pem + "+");
 +
 +      props.put(getAudienceProperty(), "bar");
 +      props.put("sso.authentication.provider.url", "https://localhost:8443/gateway/knoxsso/api/v1/websso");
 +      props.put(getVerificationPemProperty(), pem);
 +      handler.init(new TestFilterConfig(props));
 +
-       SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 50000), privateKey, props);
++      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
++                             new Date(new Date().getTime() + 50000), privateKey);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal", !principals.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 testExpiredJWT() throws Exception {
 +    try {
 +      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);
 +
 +      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.");
 +    }
 +  }
 +
 +  @Test
 +  public void testValidJWTNoExpiration() throws Exception {
 +    try {
 +      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);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL).anyTimes();
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      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 testUnableToParseJWT() throws Exception {
 +    try {
 +      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);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL).anyTimes();
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testFailedSignatureValidationJWT() throws Exception {
 +    try {
 +      // Create a private key to sign the token
 +      KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
 +      kpg.initialize(1024);
 +
 +      KeyPair kp = kpg.genKeyPair();
 +
 +      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);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL).anyTimes();
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testInvalidVerificationPEM() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +
 +      KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
 +      kpg.initialize(1024);
 +
 +      KeyPair KPair = kpg.generateKeyPair();
 +      String dn = buildDistinguishedName(InetAddress.getLocalHost().getHostName());
 +      Certificate cert = X509CertificateUtil.generateCertificate(dn, KPair, 365, "SHA1withRSA");
 +      byte[] data = cert.getEncoded();
 +      Base64 encoder = new Base64( 76, "\n".getBytes( "ASCII" ) );
 +      String failingPem = new String(encoder.encodeToString( data ).getBytes( "ASCII" )).trim();
 +
 +      props.put(getAudienceProperty(), "bar");
 +      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);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", chain.doFilterCalled == false);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testInvalidIssuer() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("new-issuer", "alice", new Date(new Date().getTime() + 5000), privateKey);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +         new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be true.", !chain.doFilterCalled);
 +      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
 +  @Test
 +  public void testValidIssuerViaConfig() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      props.setProperty(AbstractJWTFilter.JWT_EXPECTED_ISSUER, "new-issuer");
 +      handler.init(new TestFilterConfig(props));
 +
 +      SignedJWT jwt = getJWT("new-issuer", "alice", new Date(new Date().getTime() + 5000), privateKey);
 +
 +      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +      setTokenOnRequest(request, jwt);
 +
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled);
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal", principals.size() > 0);
 +      Assert.assertEquals("Not the expected principal", "alice", ((Principal)principals.toArray()[0]).getName());
 +    } catch (ServletException se) {
 +      fail("Should NOT have thrown a ServletException.");
 +    }
 +  }
 +
++  @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),
++                             new Date(), 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),
++                             new Date(), 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.");
++    }
++  }
++
++  @Test
++  public void testNotBeforeJWT() 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),
++                             new Date(new Date().getTime() + 5000), privateKey,
++                             JWSAlgorithm.RS256.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(
 +        SSOCookieFederationFilter.SSO_AUTHENTICATION_PROVIDER_URL,
 +        "https://localhost:8443/authserver");
 +    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, new Date(), privateKey, JWSAlgorithm.RS256.getName());
++  }
++
++  protected SignedJWT getJWT(String issuer, String sub, Date expires, Date nbf, RSAPrivateKey privateKey,
++                             String signatureAlgorithm)
++      throws Exception {
++    return getJWT(issuer, sub, "bar", expires, nbf, privateKey, signatureAlgorithm);
 +  }
 +
-   protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey)
++  protected SignedJWT getJWT(String issuer, String sub, String aud, Date expires, Date nbf, RSAPrivateKey privateKey,
++                             String signatureAlgorithm)
 +      throws Exception {
-     List<String> aud = new ArrayList<String>();
-     aud.add("bar");
++    List<String> audiences = new ArrayList<String>();
++    if (aud != null) {
++      audiences.add(aud);
++    }
 +
 +    JWTClaimsSet claims = new JWTClaimsSet.Builder()
 +    .issuer(issuer)
 +    .subject(sub)
 +    .audience(aud)
 +    .expirationTime(expires)
++    .notBeforeTime(nbf)
 +    .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);
 +
 +    signedJWT.sign(signer);
 +
 +    return signedJWT;
 +  }
 +
 +  protected static class TestFilterConfig implements FilterConfig {
 +    Properties props = null;
 +
 +    public TestFilterConfig(Properties props) {
 +      this.props = props;
 +    }
 +
 +    @Override
 +    public String getFilterName() {
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see javax.servlet.FilterConfig#getServletContext()
 +     */
 +    @Override
 +    public ServletContext getServletContext() {
 +//      JWTokenAuthority authority = EasyMock.createNiceMock(JWTokenAuthority.class);
 +//      GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
 +//      EasyMock.expect(services.getService("TokenService").andReturn(authority));
 +//      ServletContext context = EasyMock.createNiceMock(ServletContext.class);
 +//      EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE).andReturn(new DefaultGatewayServices()));
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see javax.servlet.FilterConfig#getInitParameter(java.lang.String)
 +     */
 +    @Override
 +    public String getInitParameter(String name) {
 +      return props.getProperty(name, null);
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see javax.servlet.FilterConfig#getInitParameterNames()
 +     */
 +    @Override
 +    public Enumeration<String> getInitParameterNames() {
 +      return null;
 +    }
 +
 +  }
 +
 +  protected static class TestJWTokenAuthority implements JWTokenAuthority {
 +
 +    private PublicKey verifyingKey;
 +
 +    public TestJWTokenAuthority(PublicKey verifyingKey) {
 +      this.verifyingKey = verifyingKey;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(javax.security.auth.Subject, java.lang.String)
 +     */
 +    @Override
 +    public JWT issueToken(Subject subject, String algorithm)
 +        throws TokenServiceException {
 +      // TODO Auto-generated method stub
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String)
 +     */
 +    @Override
 +    public JWT issueToken(Principal p, String algorithm)
 +        throws TokenServiceException {
 +      // TODO Auto-generated method stub
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, java.lang.String)
 +     */
 +    @Override
 +    public JWT issueToken(Principal p, String audience, String algorithm)
 +        throws TokenServiceException {
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see org.apache.knox.gateway.services.security.token.JWTokenAuthority#verifyToken(org.apache.knox.gateway.services.security.token.impl.JWT)
 +     */
 +    @Override
 +    public boolean verifyToken(JWT token) throws TokenServiceException {
 +      JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) verifyingKey);
 +      return token.verify(verifier);
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, java.lang.String, long)
 +     */
 +    @Override
 +    public JWT issueToken(Principal p, String audience, String algorithm,
 +        long expires) throws TokenServiceException {
 +      return null;
 +    }
 +
 +    @Override
 +    public JWT issueToken(Principal p, List<String> audiences, String algorithm,
 +        long expires) throws TokenServiceException {
 +      return null;
 +    }
 +
 +    /* (non-Javadoc)
 +     * @see JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, long)
 +     */
 +    @Override
 +    public JWT issueToken(Principal p, String algorithm, long expires)
 +        throws TokenServiceException {
 +      // TODO Auto-generated method stub
 +      return null;
 +    }
 +
 +    @Override
 +    public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException {
 +      JWSVerifier verifier = new RSASSAVerifier(publicKey);
 +      return token.verify(verifier);
 +    }
 +
 +  }
 +
 +  protected static class TestFilterChain implements FilterChain {
 +    boolean doFilterCalled = false;
 +    Subject subject = null;
 +
 +    /* (non-Javadoc)
 +     * @see javax.servlet.FilterChain#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
 +     */
 +    @Override
 +    public void doFilter(ServletRequest request, ServletResponse response)
 +        throws IOException, ServletException {
 +      doFilterCalled = true;
 +
 +      subject = Subject.getSubject( AccessController.getContext() );
 +    }
 +
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
index babbee2,0000000..50a44ce
mode 100644,000000..100644
--- a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/SSOCookieProviderTest.java
@@@ -1,161 -1,0 +1,162 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.provider.federation;
 +
 +import static org.junit.Assert.fail;
 +
 +import java.security.NoSuchAlgorithmException;
 +import java.security.Principal;
 +import java.util.Properties;
 +import java.util.Date;
 +import java.util.Set;
 +
 +import javax.servlet.ServletException;
 +import javax.servlet.http.Cookie;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +
++import org.apache.knox.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
 +import org.apache.knox.gateway.provider.federation.jwt.filter.SSOCookieFederationFilter;
 +import org.apache.knox.gateway.security.PrimaryPrincipal;
 +import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 +import org.easymock.EasyMock;
 +import org.junit.Assert;
 +import org.junit.Before;
 +import org.junit.Test;
 +
 +import com.nimbusds.jwt.SignedJWT;
 +
 +public class SSOCookieProviderTest extends AbstractJWTFilterTest {
 +  private static final String SERVICE_URL = "https://localhost:8888/resource";
 +
 +  @Before
 +  public void setup() throws Exception, NoSuchAlgorithmException {
 +    handler = new TestSSOCookieFederationProvider();
 +    ((TestSSOCookieFederationProvider) handler).setTokenService(new TestJWTokenAuthority(publicKey));
 +  }
 +
 +  protected void setTokenOnRequest(HttpServletRequest request, SignedJWT jwt) {
 +    Cookie cookie = new Cookie("hadoop-jwt", jwt.serialize());
 +    EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie });
 +  }
 +
 +  protected void setGarbledTokenOnRequest(HttpServletRequest request, SignedJWT jwt) {
 +    Cookie cookie = new Cookie("hadoop-jwt", "ljm" + jwt.serialize());
 +    EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie });
 +  }
 +
 +  protected String getAudienceProperty() {
 +    return TestSSOCookieFederationProvider.SSO_EXPECTED_AUDIENCES;
 +  }
 +
 +  @Test
 +  public void testCustomCookieNameJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      props.put("sso.cookie.name", "jowt");
 +      handler.init(new TestFilterConfig(props));
 +
-       SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000),
-           privateKey, props);
++      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);
 +      EasyMock.expect(request.getCookies()).andReturn(new Cookie[] { cookie });
 +      EasyMock.expect(request.getRequestURL()).andReturn(
 +          new StringBuffer(SERVICE_URL)).anyTimes();
 +      EasyMock.expect(request.getQueryString()).andReturn(null);
 +      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
 +      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
 +          SERVICE_URL);
 +      EasyMock.replay(request);
 +
 +      TestFilterChain chain = new TestFilterChain();
 +      handler.doFilter(request, response, chain);
 +      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
 +      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
 +      Assert.assertTrue("No PrimaryPrincipal returned.", !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 testNoProviderURLJWT() throws Exception {
 +    try {
 +      Properties props = getProperties();
 +      props.remove("sso.authentication.provider.url");
 +      handler.init(new TestFilterConfig(props));
 +
 +      fail("Servlet exception should have been thrown.");
 +
 +    } catch (ServletException se) {
 +      // expected - let's ensure it mentions the missing authentication provider URL
 +      se.getMessage().contains("authentication provider URL is missing");
 +    }
 +  }
 +
 +  @Test
 +  public void testOrigURLWithQueryString() throws Exception {
 +    Properties props = getProperties();
 +    handler.init(new TestFilterConfig(props));
 +
 +    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +    EasyMock.expect(request.getRequestURL()).andReturn(
 +        new StringBuffer(SERVICE_URL)).anyTimes();
 +    EasyMock.expect(request.getQueryString()).andReturn("name=value");
 +    EasyMock.replay(request);
 +
 +    String loginURL = ((TestSSOCookieFederationProvider)handler).testConstructLoginURL(request);
 +    Assert.assertNotNull("loginURL should not be null.", loginURL);
 +    Assert.assertEquals("https://localhost:8443/authserver?originalUrl=" + SERVICE_URL + "?name=value", loginURL);
 +  }
 +
 +  @Test
 +  public void testOrigURLNoQueryString() throws Exception {
 +    Properties props = getProperties();
 +    handler.init(new TestFilterConfig(props));
 +
 +    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
 +    EasyMock.expect(request.getRequestURL()).andReturn(
 +        new StringBuffer(SERVICE_URL)).anyTimes();
 +    EasyMock.expect(request.getQueryString()).andReturn(null);
 +    EasyMock.replay(request);
 +
 +    String loginURL = ((TestSSOCookieFederationProvider)handler).testConstructLoginURL(request);
 +    Assert.assertNotNull("LoginURL should not be null.", loginURL);
 +    Assert.assertEquals("https://localhost:8443/authserver?originalUrl=" + SERVICE_URL, loginURL);
 +  }
 +
 +
 +  @Override
 +  protected String getVerificationPemProperty() {
 +    return SSOCookieFederationFilter.SSO_VERIFICATION_PEM;
 +  };
 +
 +  private static class TestSSOCookieFederationProvider extends SSOCookieFederationFilter {
 +    public String testConstructLoginURL(HttpServletRequest req) {
 +      return constructLoginURL(req);
 +    }
 +
 +    public void setTokenService(JWTokenAuthority ts) {
 +      authority = ts;
 +    }
 +  };
 +
 +}