/*
 * Copyright © 2015 Cask Data, Inc.
 *
 * Licensed 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 co.cask.cdap.gateway.router;

import co.cask.cdap.api.common.Bytes;
import co.cask.cdap.common.conf.Constants;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
import org.apache.twill.discovery.DiscoveryService;
import org.apache.twill.discovery.InMemoryDiscoveryService;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestRule;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;

/**
 * Verify the ordering of events in the RouterPipeline.
 */
public class NettyRouterPipelineAuthTest {

  private static final String HOSTNAME = "127.0.0.1";

  private static final String GATEWAY_NAME = Constants.Router.GATEWAY_DISCOVERY_NAME;
  private static final String SERVICE_NAME = Constants.Service.APP_FABRIC_HTTP;

  private static final DiscoveryService DISCOVERY_SERVICE = new InMemoryDiscoveryService();
  public static final RouterResource ROUTER = new RouterResource(HOSTNAME, DISCOVERY_SERVICE, ImmutableMap.of(
    Constants.Security.ENABLED, "true",
    Constants.Security.Router.BYPASS_AUTHENTICATION_REGEX, "(/v1/repeat/.*|/v1/echo/dontfail)"
  ));
  public static final ServerResource GATEWAY_SERVER = new ServerResource(HOSTNAME, DISCOVERY_SERVICE, SERVICE_NAME);

  @ClassRule
  public static final TemporaryFolder TMP_FOLDER = new TemporaryFolder();

  @SuppressWarnings("UnusedDeclaration")
  @ClassRule
  public static TestRule chain = RuleChain.outerRule(ROUTER).around(GATEWAY_SERVER);

  @Test
  public void testRouterAuthBypass() throws Exception {
    // mock token validator passes for no token and any token other than "Bearer failme"
    testGet(200, "hello"   , "/v1/echo/hello");
    testGet(200, "hello"   , "/v1/echo/hello", ImmutableMap.of("Authorization", "Bearer x"));
    // so this should fail
    testGet(401, null      , "/v1/echo/hello", ImmutableMap.of("Authorization", "Bearer failme"));
    // but /v1/echo/dontfail is configured to bypass auth
    testGet(200, "dontfail", "/v1/echo/dontfail", ImmutableMap.of("Authorization", "Bearer failme"));
    // it only bypasses on exact match, not prefix match
    testGet(401, null      , "/v1/echo/dontfailme", ImmutableMap.of("Authorization", "Bearer failme"));

    // /v1/repeat is configured to bypass auth validation, on prefix match
    testGet(200, "hello", "/v1/repeat/hello");
    testGet(200, "hello", "/v1/repeat/hello", ImmutableMap.of("Authorization", "Bearer x"));
    testGet(200, "hello", "/v1/repeat/hello", ImmutableMap.of("Authorization", "Bearer failme"));
    // even with a token that fails validation, we get the correct status code 404
    testGet(404, null   , "/v1/repeat/dontfail/me", ImmutableMap.of("Authorization", "Bearer failme"));
  }

  private void testGet(int expectedStatus, String expectedResponse, String path) throws Exception {
    testGet(expectedStatus, expectedResponse, path, Collections.<String, String>emptyMap());
  }

  private void testGet(int expectedStatus, String expectedResponse, String path, Map<String, String> headers)
    throws Exception {

    URL url = new URL("http", HOSTNAME, ROUTER.getServiceMap().get(GATEWAY_NAME), path);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("GET");
    connection.setDoInput(true);
    for (Map.Entry<String, String> header : headers.entrySet()) {
      connection.setRequestProperty(header.getKey(), header.getValue());
    }
    connection.connect();
    try {
      Assert.assertEquals(expectedStatus, connection.getResponseCode());
      if (expectedResponse != null) {
        byte[] content = ByteStreams.toByteArray(connection.getInputStream());
        Assert.assertEquals(expectedResponse, Bytes.toString(content));
      }
    } finally {
      connection.disconnect();
    }
  }

}

@@ -2392,35 +2392,32 @@
 Get(200, %22hello%22
-   
 , %22/v1/echo/hell
@@ -2445,19 +2445,16 @@
  %22hello%22
-   
 , %22/v1/e
@@ -2552,38 +2552,32 @@
 estGet(401, null
-      
 , %22/v1/echo/hell
@@ -2870,22 +2870,16 @@
 01, null
-      
 , %22/v1/e
@@ -3365,19 +3365,16 @@
 04, null
-   
 , %22/v1/r

address comments