All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mle.play.controllers.BaseSecurity.scala Maven / Gradle / Ivy

The newest version!
package com.mle.play.controllers

import java.nio.file.{Files, Path, Paths}

import com.mle.concurrent.FutureOps
import com.mle.play.auth.{Auth, BasicCredentials}
import com.mle.util.Log
import play.api.libs.iteratee.{Done, Input, Iteratee}
import play.api.libs.{Files => PlayFiles}
import play.api.mvc.Results._
import play.api.mvc.Security.AuthenticatedRequest
import play.api.mvc._

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

/**
 *
 * @author mle
 */
class AuthRequest[A](user: String, request: Request[A], val cookie: Option[Cookie] = None)
  extends AuthenticatedRequest[A, String](user, request)

class FileUploadRequest[A](val files: Seq[Path], user: String, request: Request[A]) extends AuthRequest(user, request)

class OneFileUploadRequest[A](val file: Path, user: String, request: Request[A]) extends AuthRequest(user, request)

case class AuthResult(user: String, cookie: Option[Cookie] = None)

trait BaseSecurity extends Log {


  def authenticateFromSession(implicit request: RequestHeader): Option[String] =
    request.session.get(Security.username) //.filter(_.nonEmpty)

  def authenticateFromHeader(implicit request: RequestHeader): Option[String] =
    headerAuth(validateCredentials)

  /**
   * Basic HTTP authentication.
   *
   * The "Authorization" request header should be like: "Basic base64(username:password)", where
   * base64(x) means x base64-encoded.
   *
   * @param f credentials verifier: returns true on success, false otherwise
   * @param request request from which the Authorization header is validated
   * @return the username wrapped in an Option if successfully authenticated, None otherwise
   */
  def headerAuth(f: BasicCredentials => Boolean)(implicit request: RequestHeader): Option[String] = {
    Auth.basicCredentials(request).filter(f).map(_.username)
  }

  /**
   * Authenticates based on the "u" and "p" query string parameters.
   *
   * @param request request
   * @return the username, if successfully authenticated
   */
  def authenticateFromQueryString(implicit request: RequestHeader): Option[String] = {
    Auth.credentialsFromQuery(request).filter(validateCredentials).map(_.username)
  }

  /**
   * Override if you intend to use password authentication.
   *
   * @return True if the credentials are valid; false otherwise. False by default.
   */
  def validateCredentials(creds: BasicCredentials): Boolean = false

  /**
   * Retrieves the authenticated username from the request.
   *
   * Attempts to read the "username" session variable, but if no such thing exists,
   * attempts to authenticate based on the the HTTP Authorization header,
   * finally if that also fails, authenticates based on credentials in the query string.
   *
   * @return the authentication result wrapped in an [[scala.Option]] if successfully authenticated, [[scala.None]] otherwise
   */
  def authenticate(implicit request: RequestHeader): Option[AuthResult] = {
    (authenticateFromSession orElse
      authenticateFromHeader orElse
      authenticateFromQueryString) map lift
  }

  /**
   * Called when an unauthorized request has been made. Also
   * called when a failed authentication attempt is made.
   *
   * Returns HTTP 401 by default; override to handle unauthorized
   * requests in a more app-specific manner.
   *
   * @param req header of request which failed authentication
   * @return "auth failed" result
   */
  protected def onUnauthorized(implicit req: RequestHeader): Result = {
    val ip = req.remoteAddress
    val resource = req.path
    log warn s"Unauthorized request to: $resource from: $ip"
    Unauthorized
  }

  /**
   *
   * @param authFunction
   * @param authAction
   * @tparam U type of user
   * @return
   */
  def LoggedSecureAction[U](authFunction: RequestHeader => Option[U])(authAction: U => EssentialAction): EssentialAction =
    Security.Authenticated(req => authFunction(req), req => onUnauthorized(req))(user => Logged(authAction(user)))

  def AuthActionAsync(f: AuthRequest[AnyContent] => Future[Result]) =
    AuthenticatedLogged(user => Action.async(req => f(new AuthRequest(user.user, req, user.cookie))))

  def LoggedSecureActionAsync[U](authFunction: RequestHeader => Future[U])(authAction: U => EssentialAction) =
    AuthenticatedAsync(authFunction, req => onUnauthorized(req))(user => Logged(authAction(user)))

  def AuthenticatedAsync[A](authFunction: RequestHeader => Future[A],
                            onUnauthorized: RequestHeader => Result)(action: A => EssentialAction): EssentialAction = {
    val f2: RequestHeader => Future[Option[A]] = req => authFunction(req).map(a => Some(a)).recoverAll(_ => None)
    AuthenticatedAsync2(f2, onUnauthorized)(action)
  }

  def AuthenticatedAsync2[A](authFunction: RequestHeader => Future[Option[A]],
                             onUnauthorized: RequestHeader => Result)(action: A => EssentialAction): EssentialAction = {
    EssentialAction(request => {
      val futureIteratee: Future[Iteratee[Array[Byte], Result]] = authFunction(request)
        .map(userOpt => userOpt.map(user => action(user)(request))
        .getOrElse(Done(onUnauthorized(request), Input.Empty)))
      Iteratee flatten futureIteratee
    })
  }

  def Authenticated(f: AuthResult => EssentialAction): EssentialAction =
    Security.Authenticated(req => authenticate(req), unAuthorizedRequest => onUnauthorized(unAuthorizedRequest))(f)

  def Authenticated(f: => EssentialAction): EssentialAction = Authenticated(user => f)

  def AuthenticatedLogged(f: AuthResult => EssentialAction): EssentialAction = Authenticated(user => Logged(user, f))

  def AuthenticatedLogged(f: => EssentialAction): EssentialAction = AuthenticatedLogged(_ => f)

  def AuthAction(f: AuthRequest[AnyContent] => Result) =
    AuthenticatedLogged(user => Action(req => f(new AuthRequest(user.user, req, user.cookie))))

  /**
   * Logs authenticated requests.
   */
  def Logged(user: AuthResult, f: AuthResult => EssentialAction) =
    EssentialAction(request => {
      val qString = request.rawQueryString
      // removes query string from logged line if it contains a password, assumes password is in 'p' parameter
      def queryString =
        if (qString != null && qString.length > 0 && !qString.contains("p=")) s"?$qString"
        else ""
      log info s"User: ${user.user} from: ${request.remoteAddress} requests: ${request.path}$queryString"
      f(user)(request)
    })

  def Logged(action: EssentialAction): EssentialAction = EssentialAction(req => {
    log debug s"Request: ${req.path} from: ${req.remoteAddress}"
    action(req)
  })

  val uploadDir = Paths get sys.props("java.io.tmpdir")

  protected def saveFiles(request: Request[MultipartFormData[PlayFiles.TemporaryFile]]): Seq[Path] =
    request.body.files.map(file => {
      val dest = uploadDir resolve file.filename
      if (!Files.exists(dest))
        file.ref.moveTo(dest.toFile, replace = true)
      dest
    })

  protected def lift(user: String) = AuthResult(user)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy