/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2013-2015 ForgeRock AS
 */
package org.opends.server.protocols.http;

import static org.mockito.Mockito.mock;
import static org.assertj.core.api.Assertions.*;
import static org.opends.server.protocols.http.CollectClientConnectionsFilter.*;

import java.io.IOException;

import org.assertj.core.api.SoftAssertions;
import org.forgerock.http.protocol.Request;
import org.forgerock.http.protocol.Response;
import org.forgerock.json.resource.ResourceException;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.core.ServerContext;
import org.opends.server.util.Base64;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

@SuppressWarnings("javadoc")
public class CollectClientConnectionsFilterTest extends DirectoryServerTestCase
{
  private static final String USERNAME = "Aladdin";
  private static final String PASSWORD = "open sesame";
  private static final String BASE64_USERPASS = Base64.encode((USERNAME + ":" + PASSWORD).getBytes());

  private HTTPAuthenticationConfig authConfig;
  private CollectClientConnectionsFilter filter;

  @BeforeMethod
  private void createConfigAndFilter()
  {
    authConfig = new HTTPAuthenticationConfig();
    filter = new CollectClientConnectionsFilter(mock(ServerContext.class), null, authConfig);
  }

  @DataProvider(name = "Invalid HTTP basic auth strings")
  public Object[][] getInvalidHttpBasicAuthStrings()
  {
    return new Object[][] { { null }, { "bla" }, { "basic " + Base64.encode("la:bli:blu".getBytes()) } };
  }

  @Test(dataProvider = "Invalid HTTP basic auth strings")
  public void parseUsernamePasswordFromInvalidAuthZHeader(String authZHeader) throws Exception
  {
    assertThat(filter.parseUsernamePassword(authZHeader)).isNull();
  }

  @DataProvider(name = "Valid HTTP basic auth strings")
  public Object[][] getValidHttpBasicAuthStrings()
  {
    return new Object[][] { { "basic " + BASE64_USERPASS }, { "Basic " + BASE64_USERPASS } };
  }

  @Test(dataProvider = "Valid HTTP basic auth strings")
  public void parseUsernamePasswordFromValidAuthZHeader(String authZHeader) throws Exception
  {
    assertThat(filter.parseUsernamePassword(authZHeader)).containsExactly(USERNAME, PASSWORD);
  }

  @Test
  public void sendUnauthorizedResponseWithHttpBasicAuthWillChallengeUserAgent() throws Exception
  {
    authConfig.setBasicAuthenticationSupported(true);
    final Response response = sendUnauthorizedResponseWithHTTPBasicAuthChallenge();

    assertThat(response.getHeaders().getFirst("WWW-Authenticate")).isEqualTo("Basic realm=\"org.forgerock.opendj\"");
    verifyUnauthorizedOutputMessage(response);
  }

  @Test
  public void sendUnauthorizedResponseWithoutHttpBasicAuthWillNotChallengeUserAgent() throws Exception
  {
    authConfig.setBasicAuthenticationSupported(false);
    final Response response = sendUnauthorizedResponseWithHTTPBasicAuthChallenge();

    assertThat(response.getHeaders().getFirst("WWW-Authenticate")).isNull();
    verifyUnauthorizedOutputMessage(response);
  }

  private Response sendUnauthorizedResponseWithHTTPBasicAuthChallenge() throws Exception
  {
    return filter.resourceExceptionToPromise(ResourceException.getException(401, "Invalid Credentials")).get();
  }

  private void verifyUnauthorizedOutputMessage(Response response) throws IOException
  {
    final SoftAssertions softly = new SoftAssertions();
    softly.assertThat(response.getStatus().getCode()).isEqualTo(401);
    softly.assertThat(response.getStatus().getReasonPhrase()).isEqualTo("Unauthorized");
    softly.assertThat(response.getEntity().getJson().toString()).isEqualTo(
            "{code=401, reason=Unauthorized, message=Invalid Credentials}");
    softly.assertAll();
  }

  @Test
  public void extractUsernamePasswordHttpBasicAuthWillAcceptUserAgent() throws Exception
  {
    authConfig.setBasicAuthenticationSupported(true);

    final Request request = new Request();
    request.getHeaders().add(HTTP_BASIC_AUTH_HEADER, "Basic " + BASE64_USERPASS);
    assertThat(filter.extractUsernamePassword(request)).containsExactly(USERNAME, PASSWORD);
  }

  @Test
  public void extractUsernamePasswordCustomHeaders() throws Exception
  {
    final String customHeaderUsername = "X-OpenIDM-Username";
    final String customHeaderPassword = "X-OpenIDM-Password";

    authConfig.setCustomHeadersAuthenticationSupported(true);
    authConfig.setCustomHeaderUsername(customHeaderUsername);
    authConfig.setCustomHeaderPassword(customHeaderPassword);

    final Request request = new Request();
    request.getHeaders().add(customHeaderUsername, USERNAME);
    request.getHeaders().add(customHeaderPassword, PASSWORD);

    assertThat(filter.extractUsernamePassword(request)).containsExactly(USERNAME, PASSWORD);
  }
}
