<commit_before>/**
 * Copyright (c) 2012 <NAME> (born 1979)
 */

package controllers

import com.debiki.v0._
import com.debiki.v0.Prelude._
import com.debiki.v0.EmailNotfPrefs.EmailNotfPrefs
import debiki._
import debiki.DebikiHttp._
import java.{util => ju}
import play.api._
import play.api.mvc.{Action => _, _}
import Actions._
import Utils.{OkHtml}


object AppAuth extends mvc.Controller {

  /**
   * Finds the session id and any xsrf token in the specified request;
   * throws an error if this is not a GET request and the xsrf token is bad.
   * @return The current SID and xsrf token. Or new ones if needed (with no
   * login id, no user, no display name), and a new SID and xsrf cookie.
   * @throws DebikiHttp.ResultException, for non-GET requests,
   * if the SID or the XSRF token is bad.
   */
  def checkSidAndXsrfToken(request: Request[_])
        : (SidOk, XsrfOk, List[Cookie]) = {

    val sidValOpt = urlDecodeCookie("dwCoSid", request)
    val sidStatus: SidStatus = sidValOpt.map(Sid.check(_)) getOrElse SidAbsent

    // On GET requests, simply accept the value of the xsrf cookie.
    // (On POST requests, however, we check the xsrf form input value)
    val xsrfValOpt = urlDecodeCookie("dwCoXsrf", request)
    var xsrfStatus: XsrfStatus = xsrfValOpt.map(XsrfOk(_)) getOrElse XsrfAbsent
    // However, we might catch some bug if we verify it matches the sid:
    if (xsrfValOpt.isDefined && sidValOpt.isDefined) {
      assert(Xsrf.check(xsrfValOpt.get, sidValOpt).isInstanceOf[XsrfOk])
    }

    val sidXsrfNewCookies: Tuple3[SidOk, XsrfOk, List[Cookie]] =
      if (request.method == "GET") {
        // Reuse SID and xsrf token, or create new ones.
        (sidStatus, xsrfStatus) match {
          case (sidOk: SidOk, xsrfOk: XsrfOk) => (sidOk, xsrfOk, Nil)
          case (_, _) => Xsrf.newSidAndXsrf(
            loginId = None, userId = None, displayName = None)
        }
      } else {
        // There must be an xsrf token in the POST:ed form data.
        assert(request.method == "POST") // for now
        val params = request.body.asInstanceOf[Map[String, Seq[String]]]
        val xsrfToken: String =
          params.get(FormHtml.XsrfInpName).map(_.head).getOrElse(
            throwForbidden("DwE0y321", "No XSRF token"))
        xsrfStatus = Xsrf.check(xsrfToken, sidValOpt)
        (sidStatus, xsrfStatus) match {
          case (sidOk: SidOk, xsrfOk: XsrfOk) => (sidOk, xsrfOk, Nil)
          case (_: SidOk, _) => throwForbidden("DwE35k3wkU9", "Bad XSRF token")
          case (_, _) => throwForbidden("DwE530Rstx90", "Bad SID")
        }
      }

    sidXsrfNewCookies
  }

  def loginSimple = ExceptionAction(parse.urlFormEncoded(maxLength = 200)) {
        request =>

    XSRF // check a token, so cannot fake logins via xsrf
    // (rather harmless though)

    def getOrDie(paramName: String, errCode: String): String =
      request.body.getOrElse(paramName, DebikiHttp.throwBadReq(errCode)).head

    val name = getOrDie("dw-fi-login-name", "DwE0k31c5")
    val email = getOrDie("dw-fi-login-email", "DwE8k3i30")
    val emailNotfInp = request.body.getOrElse("dw-fi-lgi-spl-email-ntf", "no")
    val url = getOrDie("dw-fi-login-url", "DwE64kC21")

    def failLogin(errCode: String, summary: String, details: String) =
      throwForbiddenDialog(errCode, "Login Failed", summary, details)

    if (User nameIsWeird name)
      failLogin("DwE82ckWE19", "Weird name.",
        "Please specify another name, with no weird characters.")
    if (name isEmpty)
      failLogin("DwE873k2e90", "No name specified.",
        "Please fill in your name.")
    if (email.nonEmpty && User.emailIsWeird(email))
      failLogin("DwE0432hrsk23", "Weird email.",
        "Please specify an email address with no weird characters.")

    val emailNotfPrefs: EmailNotfPrefs = emailNotfInp match {
      case "yes" =>
        if (email isEmpty)
          failLogin("DwE9UW5", "No email address.",
            "Please specify an email address, so you can receive"+
            " email notifications.")
        EmailNotfPrefs.Receive
      case "no" => EmailNotfPrefs.DontReceive
      case _ =>
        // Someone has hand crafted this request?
        throwBadReq("DwE03k1r35009", "Weird checkbox value.")
    }

    if (User urlIsWeird url)
      failLogin("DwE734krsn215", "Weird URL.",
        "Please specify a website address with no weird characters.")

    val prevSidValOpt = urlDecodeCookie("dwCoSid", request)
    val prevSid = prevSidValOpt.map(Sid.check _) getOrElse SidAbsent
    val addr = "?.?.?.?"  // TODO
    val tenantId = lookupTenantByHost(request.host)

    val loginReq = Dao.LoginRequest(
      login = Login(id = "?", prevLoginId = prevSid.loginId,
        ip = addr, date = new ju.Date, identityId = "?i"),
      identity = IdentitySimple(id = "?i", userId = "?", name = name,
        email = email, location = "", website = url))

    val loginGrant = Debiki.Dao.saveLogin(tenantId, loginReq)

    if (loginGrant.user.emailNotfPrefs != emailNotfPrefs) {
      // The user changed the "Email me if someone replies to me"<commit_msg>Fix simple login dialog email input bug.<commit_after>/**
 * Copyright (c) 2012 <NAME> (born 1979)
 */

package controllers

import com.debiki.v0._
import com.debiki.v0.Prelude._
import com.debiki.v0.EmailNotfPrefs.EmailNotfPrefs
import debiki._
import debiki.DebikiHttp._
import java.{util => ju}
import play.api._
import play.api.mvc.{Action => _, _}
import Actions._
import Utils.{OkHtml}


object AppAuth extends mvc.Controller {

  /**
   * Finds the session id and any xsrf token in the specified request;
   * throws an error if this is not a GET request and the xsrf token is bad.
   * @return The current SID and xsrf token. Or new ones if needed (with no
   * login id, no user, no display name), and a new SID and xsrf cookie.
   * @throws DebikiHttp.ResultException, for non-GET requests,
   * if the SID or the XSRF token is bad.
   */
  def checkSidAndXsrfToken(request: Request[_])
        : (SidOk, XsrfOk, List[Cookie]) = {

    val sidValOpt = urlDecodeCookie("dwCoSid", request)
    val sidStatus: SidStatus = sidValOpt.map(Sid.check(_)) getOrElse SidAbsent

    // On GET requests, simply accept the value of the xsrf cookie.
    // (On POST requests, however, we check the xsrf form input value)
    val xsrfValOpt = urlDecodeCookie("dwCoXsrf", request)
    var xsrfStatus: XsrfStatus = xsrfValOpt.map(XsrfOk(_)) getOrElse XsrfAbsent
    // However, we might catch some bug if we verify it matches the sid:
    if (xsrfValOpt.isDefined && sidValOpt.isDefined) {
      assert(Xsrf.check(xsrfValOpt.get, sidValOpt).isInstanceOf[XsrfOk])
    }

    val sidXsrfNewCookies: Tuple3[SidOk, XsrfOk, List[Cookie]] =
      if (request.method == "GET") {
        // Reuse SID and xsrf token, or create new ones.
        (sidStatus, xsrfStatus) match {
          case (sidOk: SidOk, xsrfOk: XsrfOk) => (sidOk, xsrfOk, Nil)
          case (_, _) => Xsrf.newSidAndXsrf(
            loginId = None, userId = None, displayName = None)
        }
      } else {
        // There must be an xsrf token in the POST:ed form data.
        assert(request.method == "POST") // for now
        val params = request.body.asInstanceOf[Map[String, Seq[String]]]
        val xsrfToken: String =
          params.get(FormHtml.XsrfInpName).map(_.head).getOrElse(
            throwForbidden("DwE0y321", "No XSRF token"))
        xsrfStatus = Xsrf.check(xsrfToken, sidValOpt)
        (sidStatus, xsrfStatus) match {
          case (sidOk: SidOk, xsrfOk: XsrfOk) => (sidOk, xsrfOk, Nil)
          case (_: SidOk, _) => throwForbidden("DwE35k3wkU9", "Bad XSRF token")
          case (_, _) => throwForbidden("DwE530Rstx90", "Bad SID")
        }
      }

    sidXsrfNewCookies
  }

  def loginSimple = ExceptionAction(parse.urlFormEncoded(maxLength = 200)) {
        request =>

    XSRF // check a token, so cannot fake logins via xsrf
    // (rather harmless though)

    def getOrDie(paramName: String, errCode: String): String =
      request.body.getOrElse(paramName, DebikiHttp.throwBadReq(errCode)).head

    val name = getOrDie("dw-fi-login-name", "DwE0k31c5")
    val email = getOrDie("dw-fi-login-email", "DwE8k3i30")
    val emailNotfInp =
      request.body.getOrElse("dw-fi-lgi-spl-email-ntf", List("no"))
    val url = getOrDie("dw-fi-login-url", "DwE64kC21")

    def failLogin(errCode: String, summary: String, details: String) =
      throwForbiddenDialog(errCode, "Login Failed", summary, details)

    if (User nameIsWeird name)
      failLogin("DwE82ckWE19", "Weird name.",
        "Please specify another name, with no weird characters.")
    if (name isEmpty)
      failLogin("DwE873k2e90", "No name specified.",
        "Please fill in your name.")
    if (email.nonEmpty && User.emailIsWeird(email))
      failLogin("DwE0432hrsk23", "Weird email.",
        "Please specify an email address with no weird characters.")

    val emailNotfPrefs: EmailNotfPrefs = emailNotfInp match {
      case List("yes") =>
        if (email isEmpty)
          failLogin("DwE9UW5", "No email address.",
            "Please specify an email address, so you can receive"+
            " email notifications.")
        EmailNotfPrefs.Receive
      case List("no") => EmailNotfPrefs.DontReceive
      case _ =>
        // Someone has hand crafted this request?
        throwBadReq("DwE03k1r35009", "Weird checkbox value.")
    }

    if (User urlIsWeird url)
      failLogin("DwE734krsn215", "Weird URL.",
        "Please specify a website address with no weird characters.")

    val prevSidValOpt = urlDecodeCookie("dwCoSid", request)
    val prevSid = prevSidValOpt.map(Sid.check _) getOrElse SidAbsent
    val addr = "?.?.?.?"  // TODO
    val tenantId = lookupTenantByHost(request.host)

    val loginReq = Dao.LoginRequest(
      login = Login(id = "?", prevLoginId = prevSid.loginId,
        ip = addr, date = new ju.Date, identityId = "?i"),
      identity = IdentitySimple(id = "?i", userId = "?", name = name,
        email = email, location = "", website = url))

    val loginGrant = Debiki.Dao.saveLogin(tenantId, loginReq)

    if (loginGrant.user.emailNotfPrefs != emailNotfPrefs) {
      // The user changed the "Email me if someone replies to me"
{'fraction_non_alphanumeric': 0.11523075421992389, 'fraction_numerical': 0.014050151234266757, 'mean_word_length': 2.997659906396256, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7562223', 'n_tokens_mistral': 3588, 'n_tokens_neox': 3429, 'n_words': 945}