/*
 * 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.hadoopauth.filter;

import static org.easymock.EasyMock.anyString;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.captureInt;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createMockBuilder;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.getCurrentArguments;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.apache.knox.gateway.GatewayFilter;
import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
import org.apache.knox.gateway.provider.federation.jwt.filter.JWTFederationFilter;
import org.apache.knox.gateway.provider.federation.jwt.filter.SignatureVerificationCache;
import org.apache.knox.gateway.services.GatewayServices;
import org.apache.knox.gateway.services.security.AliasService;
import org.apache.knox.gateway.topology.Topology;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;

import javax.security.auth.Subject;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.AccessController;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class HadoopAuthFilterTest {
  private static final String SERVICE_URL = "https://localhost:8888/gateway/sandbox";
  private static final String JWKS_PATH = "/knoxtoken/api/v1/jwks.json";

  @Test
  public void testHadoopAuthFilterAliasesOnTopologyLevel() throws Exception {
    testHadoopAuthFilterAliases(true);
  }

  private void testHadoopAuthFilterAliases(boolean topologyLevel) throws Exception {
    String aliasKey = "signature.secret";
    String aliasConfigKey = "${ALIAS=" + aliasKey + "}";
    String aliasValue = "password";
    String clusterName = "Sample";

    Topology topology = new Topology();
    topology.setName(clusterName);

    AliasService as = createMock(AliasService.class);
    if (topologyLevel) {
      expect(as.getPasswordFromAliasForCluster(clusterName, aliasKey)).andReturn(aliasValue.toCharArray()).anyTimes();
    } else {
      expect(as.getPasswordFromAliasForCluster(clusterName, aliasKey)).andReturn(null).anyTimes();
      expect(as.getPasswordFromAliasForGateway(aliasKey)).andReturn(aliasValue.toCharArray()).anyTimes();
    }

    String configPrefix = "hadoop.auth.config.";

    Map<String, String> props = new HashMap<>();
    props.put("clusterName", clusterName);
    props.put(configPrefix + "signature.secret", aliasConfigKey);
    props.put(configPrefix + "test", "abc");

    FilterConfig filterConfig = createMock(FilterConfig.class);
    expect(filterConfig.getInitParameter(anyString()))
        .andAnswer(() -> props.get(getCurrentArguments()[0].toString()))
        .atLeastOnce();
    expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(props.keySet())).atLeastOnce();

    replay(filterConfig, as);

    HadoopAuthFilter hadoopAuthFilter = new HadoopAuthFilter();

    Properties configuration = hadoopAuthFilter.getConfiguration(as, configPrefix, filterConfig);
    assertEquals(aliasValue, configuration.getProperty(aliasKey));
    assertEquals("abc", configuration.getProperty("test"));

    verify(filterConfig, as);
  }

  @Test
  public void testHadoopAuthFilterAliasesOnGatewayLevel() throws Exception {
    testHadoopAuthFilterAliases(false);
  }

  @Test
  public void testHadoopAuthFilterIgnoreDoAs() throws Exception {
    Topology topology = new Topology();
    topology.setName("Sample");

    ServletContext servletContext = createMock(ServletContext.class);
    expect(servletContext.getAttribute("signer.secret.provider.object")).andReturn(null).atLeastOnce();

    FilterConfig filterConfig = createMock(FilterConfig.class);
    expect(filterConfig.getInitParameter("config.prefix"))
        .andReturn("some.prefix")
        .atLeastOnce();
    expect(filterConfig.getInitParameterNames())
        .andReturn(Collections.enumeration(Collections.singleton(GatewayConfig.PROXYUSER_SERVICES_IGNORE_DOAS)))
        .atLeastOnce();
    expect(filterConfig.getInitParameter(GatewayConfig.PROXYUSER_SERVICES_IGNORE_DOAS))
        .andReturn("Knox, hdfs,TesT") // Spacing and case set on purpose
        .atLeastOnce();
    expect(filterConfig.getInitParameter("support.jwt")).andReturn("false").anyTimes();
    expect(filterConfig.getServletContext()).andReturn(servletContext).atLeastOnce();
    expect(filterConfig.getInitParameter("hadoop.auth.unauthenticated.path.list")).andReturn(null).anyTimes();

    Properties configProperties = createMock(Properties.class);
    expect(configProperties.getProperty("signature.secret.file")).andReturn("signature.secret.file").atLeastOnce();
    expect(configProperties.getProperty(anyString(), anyString())).andAnswer(() -> {
      Object[] args = getCurrentArguments();

      if ("type".equals(args[0])) {
        return "simple"; // This is "simple", rather than "kerberos" to avoid the super class' init logic
      } else {
        return (String) args[1];
      }
    }).atLeastOnce();

    HadoopAuthFilter hadoopAuthFilter = createMockBuilder(HadoopAuthFilter.class)
        .addMockedMethod("getConfiguration", String.class, FilterConfig.class)
        .withConstructor()
        .createMock();
    expect(hadoopAuthFilter.getConfiguration(eq("some.prefix."), eq(filterConfig)))
        .andReturn(configProperties)
        .atLeastOnce();

    replay(filterConfig, configProperties, hadoopAuthFilter, servletContext);

    hadoopAuthFilter.init(filterConfig);

    assertTrue(hadoopAuthFilter.ignoreDoAs("knox"));
    assertTrue(hadoopAuthFilter.ignoreDoAs("hdfs"));
    assertTrue(hadoopAuthFilter.ignoreDoAs("test"));
    assertTrue(hadoopAuthFilter.ignoreDoAs("TEST"));
    assertTrue(hadoopAuthFilter.ignoreDoAs(null));
    assertTrue(hadoopAuthFilter.ignoreDoAs(""));
    assertFalse(hadoopAuthFilter.ignoreDoAs("hive"));
    assertFalse(hadoopAuthFilter.ignoreDoAs("HivE"));

    verify(filterConfig, configProperties, hadoopAuthFilter, servletContext);
  }

  @Test
  public void shouldNotUseJwtFilterIfProviderParamIsNotSet() throws Exception {
    testIfJwtSupported(null);
  }

  @Test
  public void shouldNotUseJwtFilterIfProviderParamIsFalse() throws Exception {
    testIfJwtSupported("false");
  }

  @Test
  public void shouldUseJwtFilterIfProviderParamIsTrue() throws Exception {
    testIfJwtSupported("true");
  }

  @Test
  public void testUnauthenticatedList() throws Exception {
    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);

    GatewayFilter.Holder filterConfig = createMock(GatewayFilter.Holder.class);
    expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(Collections.emptyList()));
    expect(filterConfig.getInitParameter(GatewayConfig.PROXYUSER_SERVICES_IGNORE_DOAS)).andReturn("service").atLeastOnce();
    expect(filterConfig.getInitParameter("config.prefix")).andReturn("some.prefix").atLeastOnce();
    expect(filterConfig.getInitParameter("support.jwt")).andReturn("false").anyTimes();
    expect(filterConfig.getInitParameter("hadoop.auth.unauthenticated.path.list")).andReturn(null).anyTimes();


    EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
    EasyMock.expect(response.getOutputStream()).andAnswer(
        DummyServletOutputStream::new).anyTimes();
    EasyMock.expect(request.getPathInfo()).andReturn(JWKS_PATH).anyTimes();
    EasyMock.expect(request.getQueryString()).andReturn(null);

    final ServletContext servletContext = createMock(ServletContext.class);
    expect(servletContext.getAttribute("signer.secret.provider.object")).andReturn(null).atLeastOnce();
    expect(filterConfig.getServletContext()).andReturn(servletContext).atLeastOnce();

    final HadoopAuthFilter hadoopAuthFilter = createMockBuilder(HadoopAuthFilter.class).addMockedMethod("getConfiguration", String.class, FilterConfig.class).withConstructor()
        .createMock();
    final Properties config = new Properties();
    config.put("type", "simple");
    expect(hadoopAuthFilter.getConfiguration(eq("some.prefix."), eq(filterConfig))).andReturn(config).atLeastOnce();

    replay(servletContext, filterConfig, hadoopAuthFilter, request, response);

    hadoopAuthFilter.init(filterConfig);
    DummyFilterChain chain = new DummyFilterChain();
    hadoopAuthFilter.doFilter(request, response, chain);
    Assert.assertTrue("doFilterCalled should be true.", chain.doFilterCalled );
    /* make sure the principal is anonymous */
    Assert.assertEquals("anonymous", chain.subject.getPrincipals().stream().findFirst().get().getName());
  }

  /**
   * Test to check if this can be used to bypass authentication
   * @throws Exception
   */
  @Test
  public void testNegativeUnauthenticatedListSemicolon() throws Exception {
    final String request_semicolon_path = "/knoxtoken/api/v1/jwks.json;favicon.ico";
    final Capture<Integer> capturedError = EasyMock.newCapture();
    final Capture<String> capturedErrorMessage = EasyMock.newCapture();

    HttpServletRequest request_semicolon = EasyMock.createNiceMock(HttpServletRequest.class);

    GatewayFilter.Holder filterConfig = createMock(GatewayFilter.Holder.class);
    expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(Collections.emptyList()));
    expect(filterConfig.getInitParameter(GatewayConfig.PROXYUSER_SERVICES_IGNORE_DOAS)).andReturn("service").atLeastOnce();
    expect(filterConfig.getInitParameter("config.prefix")).andReturn("some.prefix").atLeastOnce();
    expect(filterConfig.getInitParameter("support.jwt")).andReturn("false").anyTimes();
    /* update the default list to use favicon.ico */
    expect(filterConfig.getInitParameter("hadoop.auth.unauthenticated.path.list")).andReturn(request_semicolon_path).anyTimes();

    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
    /* capture errors */
    response.sendError(captureInt(capturedError), capture(capturedErrorMessage));
    expectLastCall().anyTimes();
    EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
    EasyMock.expect(response.getOutputStream()).andAnswer(
        DummyServletOutputStream::new).anyTimes();


    replay(response);

    EasyMock.expect(request_semicolon.getRequestURL()).andReturn(new StringBuffer(SERVICE_URL+request_semicolon_path)).anyTimes();
    /* try attaching favicon.ico in path */
    EasyMock.expect(request_semicolon.getPathInfo()).andReturn("/knoxtoken/api/v1/token;favicon.ico").anyTimes();
    EasyMock.expect(request_semicolon.getQueryString()).andReturn(null);


    final ServletContext servletContext = createMock(ServletContext.class);
    expect(servletContext.getAttribute("signer.secret.provider.object")).andReturn(null).atLeastOnce();
    expect(filterConfig.getServletContext()).andReturn(servletContext).atLeastOnce();

    final HadoopAuthFilter hadoopAuthFilter = createMockBuilder(HadoopAuthFilter.class).addMockedMethod("getConfiguration", String.class, FilterConfig.class).withConstructor()
        .createMock();
    final Properties config = new Properties();
    config.put("type", "simple");
    expect(hadoopAuthFilter.getConfiguration(eq("some.prefix."), eq(filterConfig))).andReturn(config).atLeastOnce();

    replay(servletContext, filterConfig, hadoopAuthFilter, request_semicolon);

    hadoopAuthFilter.init(filterConfig);
    DummyFilterChain chain = new DummyFilterChain();
    hadoopAuthFilter.doFilter(request_semicolon, response, chain);
    Assert.assertFalse("doFilterCalled should be false.", chain.doFilterCalled );
    /* make sure we get 403 */
    Assert.assertEquals(403, capturedError.getValue().intValue());
    Assert.assertEquals("Authentication required", capturedErrorMessage.getValue());
  }

  /**
   * Test to check if this can be used to bypass authentication
   * @throws Exception
   */
  @Test
  public void testNegativeUnauthenticatedListQuery() throws Exception {
    final String request_semicolon_path = "/knoxtoken/api/v1/jwks.json?favicon.ico";
    final Capture<Integer> capturedError = EasyMock.newCapture();
    final Capture<String> capturedErrorMessage = EasyMock.newCapture();

    HttpServletRequest request_query = EasyMock.createNiceMock(HttpServletRequest.class);

    GatewayFilter.Holder filterConfig = createMock(GatewayFilter.Holder.class);
    expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(Collections.emptyList()));
    expect(filterConfig.getInitParameter(GatewayConfig.PROXYUSER_SERVICES_IGNORE_DOAS)).andReturn("service").atLeastOnce();
    expect(filterConfig.getInitParameter("config.prefix")).andReturn("some.prefix").atLeastOnce();
    expect(filterConfig.getInitParameter("support.jwt")).andReturn("false").anyTimes();
    /* update the default list to use favicon.ico */
    expect(filterConfig.getInitParameter("hadoop.auth.unauthenticated.path.list")).andReturn(request_semicolon_path).anyTimes();

    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
    /* capture errors */
    response.sendError(captureInt(capturedError), capture(capturedErrorMessage));
    expectLastCall().anyTimes();
    EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
    EasyMock.expect(response.getOutputStream()).andAnswer(
        DummyServletOutputStream::new).anyTimes();


    replay(response);

    EasyMock.expect(request_query.getRequestURL()).andReturn(new StringBuffer(SERVICE_URL+request_semicolon_path)).anyTimes();
    /* try attaching favicon.ico in path */
    EasyMock.expect(request_query.getPathInfo()).andReturn("/knoxtoken/api/v1/token;favicon.ico").anyTimes();
    EasyMock.expect(request_query.getQueryString()).andReturn(null);


    final ServletContext servletContext = createMock(ServletContext.class);
    expect(servletContext.getAttribute("signer.secret.provider.object")).andReturn(null).atLeastOnce();
    expect(filterConfig.getServletContext()).andReturn(servletContext).atLeastOnce();

    final HadoopAuthFilter hadoopAuthFilter = createMockBuilder(HadoopAuthFilter.class).addMockedMethod("getConfiguration", String.class, FilterConfig.class).withConstructor()
        .createMock();
    final Properties config = new Properties();
    config.put("type", "simple");
    expect(hadoopAuthFilter.getConfiguration(eq("some.prefix."), eq(filterConfig))).andReturn(config).atLeastOnce();

    replay(servletContext, filterConfig, hadoopAuthFilter, request_query);

    hadoopAuthFilter.init(filterConfig);
    DummyFilterChain chain = new DummyFilterChain();
    hadoopAuthFilter.doFilter(request_query, response, chain);
    Assert.assertFalse("doFilterCalled should be false.", chain.doFilterCalled );
    /* make sure we get 403 */
    Assert.assertEquals(403, capturedError.getValue().intValue());
    Assert.assertEquals("Authentication required", capturedErrorMessage.getValue());
  }

  /**
   * Test to check if this can be used to bypass authentication
   * @throws Exception
   */
  @Test
  public void testNegativeUnauthenticatedListAmpersand() throws Exception {
    final String request_semicolon_path = "/knoxtoken/api/v1/jwks.json&favicon.ico";
    final Capture<Integer> capturedError = EasyMock.newCapture();
    final Capture<String> capturedErrorMessage = EasyMock.newCapture();

    HttpServletRequest request_ampersand = EasyMock.createNiceMock(HttpServletRequest.class);

    GatewayFilter.Holder filterConfig = createMock(GatewayFilter.Holder.class);
    expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(Collections.emptyList()));
    expect(filterConfig.getInitParameter(GatewayConfig.PROXYUSER_SERVICES_IGNORE_DOAS)).andReturn("service").atLeastOnce();
    expect(filterConfig.getInitParameter("config.prefix")).andReturn("some.prefix").atLeastOnce();
    expect(filterConfig.getInitParameter("support.jwt")).andReturn("false").anyTimes();
    /* update the default list to use favicon.ico */
    expect(filterConfig.getInitParameter("hadoop.auth.unauthenticated.path.list")).andReturn(request_semicolon_path).anyTimes();

    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
    /* capture errors */
    response.sendError(captureInt(capturedError), capture(capturedErrorMessage));
    expectLastCall().anyTimes();
    EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
    EasyMock.expect(response.getOutputStream()).andAnswer(
        DummyServletOutputStream::new).anyTimes();


    replay(response);

    EasyMock.expect(request_ampersand.getRequestURL()).andReturn(new StringBuffer(SERVICE_URL+request_semicolon_path)).anyTimes();
    /* try attaching favicon.ico in path */
    EasyMock.expect(request_ampersand.getPathInfo()).andReturn("/knoxtoken/api/v1/token;favicon.ico").anyTimes();
    EasyMock.expect(request_ampersand.getQueryString()).andReturn(null);


    final ServletContext servletContext = createMock(ServletContext.class);
    expect(servletContext.getAttribute("signer.secret.provider.object")).andReturn(null).atLeastOnce();
    expect(filterConfig.getServletContext()).andReturn(servletContext).atLeastOnce();

    final HadoopAuthFilter hadoopAuthFilter = createMockBuilder(HadoopAuthFilter.class).addMockedMethod("getConfiguration", String.class, FilterConfig.class).withConstructor()
        .createMock();
    final Properties config = new Properties();
    config.put("type", "simple");
    expect(hadoopAuthFilter.getConfiguration(eq("some.prefix."), eq(filterConfig))).andReturn(config).atLeastOnce();

    replay(servletContext, filterConfig, hadoopAuthFilter, request_ampersand);

    hadoopAuthFilter.init(filterConfig);
    DummyFilterChain chain = new DummyFilterChain();
    hadoopAuthFilter.doFilter(request_ampersand, response, chain);
    Assert.assertFalse("doFilterCalled should be false.", chain.doFilterCalled );
    /* make sure we get 403 */
    Assert.assertEquals(403, capturedError.getValue().intValue());
    Assert.assertEquals("Authentication required", capturedErrorMessage.getValue());
  }

  /**
   * Test to check if this can be used to bypass authentication
   * @throws Exception
   */
  @Test
  public void testNegativeUnauthenticatedListDash() throws Exception {
    final String request_semicolon_path = "/knoxtoken/api/v1/jwks.json-favicon.ico";
    final Capture<Integer> capturedError = EasyMock.newCapture();
    final Capture<String> capturedErrorMessage = EasyMock.newCapture();

    HttpServletRequest request_dash = EasyMock.createNiceMock(HttpServletRequest.class);

    GatewayFilter.Holder filterConfig = createMock(GatewayFilter.Holder.class);
    expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(Collections.emptyList()));
    expect(filterConfig.getInitParameter(GatewayConfig.PROXYUSER_SERVICES_IGNORE_DOAS)).andReturn("service").atLeastOnce();
    expect(filterConfig.getInitParameter("config.prefix")).andReturn("some.prefix").atLeastOnce();
    expect(filterConfig.getInitParameter("support.jwt")).andReturn("false").anyTimes();
    /* update the default list to use favicon.ico */
    expect(filterConfig.getInitParameter("hadoop.auth.unauthenticated.path.list")).andReturn(request_semicolon_path).anyTimes();

    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
    /* capture errors */
    response.sendError(captureInt(capturedError), capture(capturedErrorMessage));
    expectLastCall().anyTimes();
    EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
    EasyMock.expect(response.getOutputStream()).andAnswer(
        DummyServletOutputStream::new).anyTimes();


    replay(response);

    EasyMock.expect(request_dash.getRequestURL()).andReturn(new StringBuffer(SERVICE_URL+request_semicolon_path)).anyTimes();
    /* try attaching favicon.ico in path */
    EasyMock.expect(request_dash.getPathInfo()).andReturn("/knoxtoken/api/v1/token;favicon.ico").anyTimes();
    EasyMock.expect(request_dash.getQueryString()).andReturn(null);


    final ServletContext servletContext = createMock(ServletContext.class);
    expect(servletContext.getAttribute("signer.secret.provider.object")).andReturn(null).atLeastOnce();
    expect(filterConfig.getServletContext()).andReturn(servletContext).atLeastOnce();

    final HadoopAuthFilter hadoopAuthFilter = createMockBuilder(HadoopAuthFilter.class).addMockedMethod("getConfiguration", String.class, FilterConfig.class).withConstructor()
        .createMock();
    final Properties config = new Properties();
    config.put("type", "simple");
    expect(hadoopAuthFilter.getConfiguration(eq("some.prefix."), eq(filterConfig))).andReturn(config).atLeastOnce();

    replay(servletContext, filterConfig, hadoopAuthFilter, request_dash);

    hadoopAuthFilter.init(filterConfig);
    DummyFilterChain chain = new DummyFilterChain();
    hadoopAuthFilter.doFilter(request_dash, response, chain);
    Assert.assertFalse("doFilterCalled should be false.", chain.doFilterCalled );
    /* make sure we get 403 */
    Assert.assertEquals(403, capturedError.getValue().intValue());
    Assert.assertEquals("Authentication required", capturedErrorMessage.getValue());
  }

  /**
   * Test to check if this can be used to bypass authentication
   * @throws Exception
   */
  @Test
  public void testNegativeUnauthenticatedListSpace() throws Exception {
    final String request_semicolon_path = "/knoxtoken/api/v1/jwks.json favicon.ico";
    final Capture<Integer> capturedError = EasyMock.newCapture();
    final Capture<String> capturedErrorMessage = EasyMock.newCapture();

    HttpServletRequest request_space = EasyMock.createNiceMock(HttpServletRequest.class);


    GatewayFilter.Holder filterConfig = createMock(GatewayFilter.Holder.class);
    expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(Collections.emptyList()));
    expect(filterConfig.getInitParameter(GatewayConfig.PROXYUSER_SERVICES_IGNORE_DOAS)).andReturn("service").atLeastOnce();
    expect(filterConfig.getInitParameter("config.prefix")).andReturn("some.prefix").atLeastOnce();
    expect(filterConfig.getInitParameter("support.jwt")).andReturn("false").anyTimes();
    /* update the default list to use favicon.ico */
    expect(filterConfig.getInitParameter("hadoop.auth.unauthenticated.path.list")).andReturn(request_semicolon_path).anyTimes();

    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
    /* capture errors */
    response.sendError(captureInt(capturedError), capture(capturedErrorMessage));
    expectLastCall().anyTimes();
    EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(SERVICE_URL);
    EasyMock.expect(response.getOutputStream()).andAnswer(
        DummyServletOutputStream::new).anyTimes();


    replay(response);

    EasyMock.expect(request_space.getRequestURL()).andReturn(new StringBuffer(SERVICE_URL+request_semicolon_path)).anyTimes();
    /* try attaching favicon.ico in path */
    EasyMock.expect(request_space.getPathInfo()).andReturn("/knoxtoken/api/v1/token;favicon.ico").anyTimes();
    EasyMock.expect(request_space.getQueryString()).andReturn(null);


    final ServletContext servletContext = createMock(ServletContext.class);
    expect(servletContext.getAttribute("signer.secret.provider.object")).andReturn(null).atLeastOnce();
    expect(filterConfig.getServletContext()).andReturn(servletContext).atLeastOnce();

    final HadoopAuthFilter hadoopAuthFilter = createMockBuilder(HadoopAuthFilter.class).addMockedMethod("getConfiguration", String.class, FilterConfig.class).withConstructor()
        .createMock();
    final Properties config = new Properties();
    config.put("type", "simple");
    expect(hadoopAuthFilter.getConfiguration(eq("some.prefix."), eq(filterConfig))).andReturn(config).atLeastOnce();

    replay(servletContext, filterConfig, hadoopAuthFilter, request_space);

    hadoopAuthFilter.init(filterConfig);
    DummyFilterChain chain = new DummyFilterChain();
    hadoopAuthFilter.doFilter(request_space, response, chain);
    Assert.assertFalse("doFilterCalled should be false.", chain.doFilterCalled );
    /* make sure we get 403 */
    Assert.assertEquals(403, capturedError.getValue().intValue());
    Assert.assertEquals("Authentication required", capturedErrorMessage.getValue());
  }

  public static class DummyFilterChain implements FilterChain {
    boolean doFilterCalled;
    Subject subject;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException {
      doFilterCalled = true;
      subject = Subject.getSubject( AccessController.getContext() );
    }
  }

  static class DummyServletOutputStream extends ServletOutputStream {
    @Override
    public void write(int b) {
    }

    @Override
    public void setWriteListener(WriteListener arg0) {
    }

    @Override
    public boolean isReady() {
      return false;
    }
  }

  private HadoopAuthFilter testIfJwtSupported(String supportJwt) throws Exception {
    final GatewayFilter.Holder filterConfig = createMock(GatewayFilter.Holder.class);
    expect(filterConfig.getInitParameterNames()).andReturn(Collections.enumeration(Collections.emptyList()));
    expect(filterConfig.getInitParameter(GatewayConfig.PROXYUSER_SERVICES_IGNORE_DOAS)).andReturn("service").atLeastOnce();
    expect(filterConfig.getInitParameter("config.prefix")).andReturn("some.prefix").atLeastOnce();
    expect(filterConfig.getInitParameter("support.jwt")).andReturn(supportJwt).anyTimes();
    expect(filterConfig.getInitParameter("hadoop.auth.unauthenticated.path.list")).andReturn(null).anyTimes();
    final boolean isJwtSupported = Boolean.parseBoolean(supportJwt);
    if (isJwtSupported) {
      expect(filterConfig.getInitParameter(JWTFederationFilter.KNOX_TOKEN_AUDIENCES)).andReturn(null).anyTimes();
      expect(filterConfig.getInitParameter(JWTFederationFilter.KNOX_TOKEN_QUERY_PARAM_NAME)).andReturn(null).anyTimes();
      expect(filterConfig.getInitParameter(JWTFederationFilter.JWKS_URL)).andReturn(null).anyTimes();
      expect(filterConfig.getInitParameter(JWTFederationFilter.TOKEN_PRINCIPAL_CLAIM)).andReturn(null).anyTimes();
      expect(filterConfig.getInitParameter(JWTFederationFilter.TOKEN_VERIFICATION_PEM)).andReturn(null).anyTimes();
      expect(filterConfig.getInitParameter(JWTFederationFilter.JWT_UNAUTHENTICATED_PATHS_PARAM)).andReturn(null).anyTimes();
      expect(filterConfig.getInitParameter(AbstractJWTFilter.JWT_EXPECTED_ISSUER)).andReturn(null).anyTimes();
      expect(filterConfig.getInitParameter(AbstractJWTFilter.JWT_EXPECTED_SIGALG)).andReturn(null).anyTimes();
      expect(filterConfig.getInitParameter(SignatureVerificationCache.TOKENS_VERIFIED_CACHE_MAX)).andReturn(null).anyTimes();
    }

    final ServletContext servletContext = createMock(ServletContext.class);
    expect(servletContext.getAttribute(GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE)).andReturn("test").anyTimes();
    expect(servletContext.getAttribute("signer.secret.provider.object")).andReturn(null).atLeastOnce();
    if (isJwtSupported) {
      expect(servletContext.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(null).anyTimes();
    }
    expect(filterConfig.getServletContext()).andReturn(servletContext).atLeastOnce();

    final HadoopAuthFilter hadoopAuthFilter = createMockBuilder(HadoopAuthFilter.class).addMockedMethod("getConfiguration", String.class, FilterConfig.class).withConstructor()
        .createMock();
    final Properties config = new Properties();
    config.put("type", "simple");
    expect(hadoopAuthFilter.getConfiguration(eq("some.prefix."), eq(filterConfig))).andReturn(config).atLeastOnce();

    replay(servletContext, filterConfig, hadoopAuthFilter);

    hadoopAuthFilter.init(filterConfig);

    assertEquals(isJwtSupported, hadoopAuthFilter.isJwtSupported());

    return hadoopAuthFilter;
  }
}
