<commit_before>package lila.security

import org.joda.time.DateTime
import ornicar.scalalib.Random
import play.api.data._
import play.api.data.Forms._
import play.api.data.validation.{ Constraint, Valid => FormValid, Invalid, ValidationError }
import play.api.mvc.RequestHeader
import reactivemongo.api.bson._
import reactivemongo.api.ReadPreference
import scala.annotation.nowarn
import scala.concurrent.duration._

import lila.common.{ ApiVersion, EmailAddress, HTTPRequest, IpAddress }
import lila.db.BSON.BSONJodaDateTimeHandler
import lila.db.dsl._
import lila.oauth.{ AccessToken, OAuthServer }
import lila.user.{ User, UserRepo }
import User.LoginCandidate

final class SecurityApi(
    userRepo: UserRepo,
    store: Store,
    firewall: Firewall,
    geoIP: GeoIP,
    authenticator: lila.user.Authenticator,
    emailValidator: EmailAddressValidator,
    tryOauthServer: lila.oauth.OAuthServer.Try,
    tor: Tor
)(implicit
    ec: scala.concurrent.ExecutionContext,
    system: akka.actor.ActorSystem
) {

  val AccessUri = "access_uri"

  lazy val usernameOrEmailForm = Form(
    single(
      "username" -> nonEmptyText
    )
  )

  lazy val loginForm = Form(
    tuple(
      "username" -> nonEmptyText, // can also be an email
      "password" -> nonEmptyText
    )
  )

  private def loadedLoginForm(candidate: Option[LoginCandidate]) =
    Form(
      mapping(
        "username" -> nonEmptyText, // can also be an email
        "password" -> nonEmptyText,
        "token"    -> optional(nonEmptyText)
      )(authenticateCandidate(candidate)) {
        case LoginCandidate.Success(user) => (user.username, "", none).some
        case _                            => none
      }.verifying(Constraint { (t: LoginCandidate.Result) =>
        t match {
          case LoginCandidate.Success(_) => FormValid
          case LoginCandidate.InvalidUsernameOrPassword =>
            Invalid(Seq(ValidationError("invalidUsernameOrPassword")))
          case err => Invalid(Seq(ValidationError(err.toString)))
        }
      })
    )

  def loadLoginForm(str: String): Fu[Form[LoginCandidate.Result]] = {
    emailValidator.validate(EmailAddress(str)) match {
      case Some(EmailAddressValidator.Acceptable(email)) =>
        authenticator.loginCandidateByEmail(email.normalize)
      case None if User.couldBeUsername(str) => authenticator.loginCandidateById(User normalize str)
      case _                                 => fuccess(none)
    }
  } map loadedLoginForm

  @nowarn("cat=unused")
  private def authenticateCandidate(candidate: Option[LoginCandidate])(
      _username: String,
      password: String,
      token: Option[String]
  ): LoginCandidate.Result =
    candidate.fold[LoginCandidate.Result](LoginCandidate.InvalidUsernameOrPassword) {
      _(User.PasswordAndToken(User.ClearPassword(password), token map User.TotpToken.apply))
    }

  def saveAuthentication(userId: User.ID, apiVersion: Option[ApiVersion])(implicit
      req: RequestHeader
  ): Fu[String] =
    userRepo mustConfirmEmail userId flatMap {
      case true => fufail(SecurityApi MustConfirmEmail userId)
      case false =>
        val sessionId = Random secureString 22
        if (tor isExitNode HTTPRequest.ipAddress(req)) logger.info(s"TOR login $userId")
        store.save(sessionId, userId, req, apiVersion, up = true, fp = none) inject sessionId
    }

  def saveSignup(userId: User.ID, apiVersion: Option[ApiVersion], fp: Option[FingerPrint])(implicit
      req: RequestHeader
  ): Funit = {
    val sessionId = lila.common.ThreadLocalRandom nextString 22
    store.save(s"SIG-$sessionId", userId, req, apiVersion, up = false, fp = fp)
  }

  def restoreUser(req: RequestHeader): Fu[Option[FingerPrintedUser]] =
    firewall.accepts(req) ?? reqSessionId(req) ?? { sessionId =>
      store.authInfo(sessionId) flatMap {
        _ ?? { d =>
          userRepo byId d.user dmap { _ map { FingerPrintedUser(_, d.hasFp) } }
        }
      }
    }

  def oauthScoped(
      req: RequestHeader,
      scopes: List[lila.oauth.OAuthScope],
      retries: Int = 2
  ): Fu[lila.oauth.OAuthServer.AuthResult] =
    tryOauthServer().flatMap {
      case None if retries > 0 =>
        lila.common.Future.delay(2 seconds) {<commit_msg>it's Tor not TOR<commit_after>package lila.security

import org.joda.time.DateTime
import ornicar.scalalib.Random
import play.api.data._
import play.api.data.Forms._
import play.api.data.validation.{ Constraint, Valid => FormValid, Invalid, ValidationError }
import play.api.mvc.RequestHeader
import reactivemongo.api.bson._
import reactivemongo.api.ReadPreference
import scala.annotation.nowarn
import scala.concurrent.duration._

import lila.common.{ ApiVersion, EmailAddress, HTTPRequest, IpAddress }
import lila.db.BSON.BSONJodaDateTimeHandler
import lila.db.dsl._
import lila.oauth.{ AccessToken, OAuthServer }
import lila.user.{ User, UserRepo }
import User.LoginCandidate

final class SecurityApi(
    userRepo: UserRepo,
    store: Store,
    firewall: Firewall,
    geoIP: GeoIP,
    authenticator: lila.user.Authenticator,
    emailValidator: EmailAddressValidator,
    tryOauthServer: lila.oauth.OAuthServer.Try,
    tor: Tor
)(implicit
    ec: scala.concurrent.ExecutionContext,
    system: akka.actor.ActorSystem
) {

  val AccessUri = "access_uri"

  lazy val usernameOrEmailForm = Form(
    single(
      "username" -> nonEmptyText
    )
  )

  lazy val loginForm = Form(
    tuple(
      "username" -> nonEmptyText, // can also be an email
      "password" -> nonEmptyText
    )
  )

  private def loadedLoginForm(candidate: Option[LoginCandidate]) =
    Form(
      mapping(
        "username" -> nonEmptyText, // can also be an email
        "password" -> nonEmptyText,
        "token"    -> optional(nonEmptyText)
      )(authenticateCandidate(candidate)) {
        case LoginCandidate.Success(user) => (user.username, "", none).some
        case _                            => none
      }.verifying(Constraint { (t: LoginCandidate.Result) =>
        t match {
          case LoginCandidate.Success(_) => FormValid
          case LoginCandidate.InvalidUsernameOrPassword =>
            Invalid(Seq(ValidationError("invalidUsernameOrPassword")))
          case err => Invalid(Seq(ValidationError(err.toString)))
        }
      })
    )

  def loadLoginForm(str: String): Fu[Form[LoginCandidate.Result]] = {
    emailValidator.validate(EmailAddress(str)) match {
      case Some(EmailAddressValidator.Acceptable(email)) =>
        authenticator.loginCandidateByEmail(email.normalize)
      case None if User.couldBeUsername(str) => authenticator.loginCandidateById(User normalize str)
      case _                                 => fuccess(none)
    }
  } map loadedLoginForm

  @nowarn("cat=unused")
  private def authenticateCandidate(candidate: Option[LoginCandidate])(
      _username: String,
      password: <PASSWORD>,
      token: Option[String]
  ): LoginCandidate.Result =
    candidate.fold[LoginCandidate.Result](LoginCandidate.InvalidUsernameOrPassword) {
      _(User.PasswordAndToken(User.ClearPassword(password), token map User.TotpToken.apply))
    }

  def saveAuthentication(userId: User.ID, apiVersion: Option[ApiVersion])(implicit
      req: RequestHeader
  ): Fu[String] =
    userRepo mustConfirmEmail userId flatMap {
      case true => fufail(SecurityApi MustConfirmEmail userId)
      case false =>
        val sessionId = Random secureString 22
        if (tor isExitNode HTTPRequest.ipAddress(req)) logger.info(s"Tor login $userId")
        store.save(sessionId, userId, req, apiVersion, up = true, fp = none) inject sessionId
    }

  def saveSignup(userId: User.ID, apiVersion: Option[ApiVersion], fp: Option[FingerPrint])(implicit
      req: RequestHeader
  ): Funit = {
    val sessionId = lila.common.ThreadLocalRandom nextString 22
    store.save(s"SIG-$sessionId", userId, req, apiVersion, up = false, fp = fp)
  }

  def restoreUser(req: RequestHeader): Fu[Option[FingerPrintedUser]] =
    firewall.accepts(req) ?? reqSessionId(req) ?? { sessionId =>
      store.authInfo(sessionId) flatMap {
        _ ?? { d =>
          userRepo byId d.user dmap { _ map { FingerPrintedUser(_, d.hasFp) } }
        }
      }
    }

  def oauthScoped(
      req: RequestHeader,
      scopes: List[lila.oauth.OAuthScope],
      retries: Int = 2
  ): Fu[lila.oauth.OAuthServer.AuthResult] =
    tryOauthServer().flatMap {
      case None if retries > 0 =>
        lila.common.Future.delay(2 seconds) {
{'fraction_non_alphanumeric': 0.10678466076696165, 'fraction_numerical': 0.0016519174041297936, 'mean_word_length': 3.250752256770311, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2391039', 'n_tokens_mistral': 2576, 'n_tokens_neox': 2555, 'n_words': 541}