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

be.objectify.deadbolt.scala.ViewSupport.scala Maven / Gradle / Ivy

There is a newer version: 2.7.1
Show newest version
package be.objectify.deadbolt.scala

import java.util.Optional
import javax.inject.{Inject, Singleton}

import be.objectify.deadbolt.core.models.Subject
import be.objectify.deadbolt.core.{DeadboltAnalyzer, PatternType}
import be.objectify.deadbolt.scala.cache.PatternCache
import play.api.mvc.Request
import play.api.{Configuration, Logger}

import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Await, Future}
import scala.util.{Failure, Success, Try}

/**
 *
 * @author Steve Chaloner ([email protected])
 */
@Singleton
class ViewSupport @Inject() (config: Configuration,
                             analyzer: ScalaAnalyzer,
                             patternCache: PatternCache,
                             listenerProvider: TemplateFailureListenerProvider,
                             ecProvider: ExecutionContextProvider) {

  val logger: Logger = Logger("deadbolt.template")

  val timeout: Long = config.getLong("deadbolt.scala.view-timeout").getOrElse(1000L)
  logger.info(s"Default timeout period for blocking views is [$timeout]ms")

  val defaultTimeout: () => Long = () => timeout

  val listener = listenerProvider.get()

  val ec: ExecutionContext = ecProvider.get()

  /**
   * Returns true if [[DeadboltHandler.getSubject()]] results in Some
   *
   * @param deadboltHandler application hook
   * @return true if the view can be accessed, otherwise false
   */
  def subjectPresent(deadboltHandler: DeadboltHandler,
                     timeoutInMillis: Long,
                     request: Request[Any]): Boolean = {
    tryToComplete(deadboltHandler.getSubject(request).map((subjectOption: Option[Subject]) => {
      subjectOption match {
        case Some(subject) => {
          Logger.logger.debug(s"handler [$deadboltHandler] :: subject is present - allowing")
          true
        }
        case None => {
          Logger.logger.debug(s"handler [$deadboltHandler] :: subject is not present - denying")
          false
        }
      }
    })(ec), timeoutInMillis)
  }

  /**
   * Returns true if [[DeadboltHandler.getSubject()]] results in None
   *
   * @param deadboltHandler application hook
   * @return true if the view can be accessed, otherwise false
   */
  def subjectNotPresent(deadboltHandler: DeadboltHandler,
                        timeoutInMillis: Long,
                        request: Request[Any]): Boolean = {
    tryToComplete(deadboltHandler.getSubject(request).map((subjectOption: Option[Subject]) => {
      subjectOption match {
        case None => {
          Logger.logger.debug(s"handler [$deadboltHandler] :: subject is not present - allowing")
          true
        }
        case Some(subject) => {
          Logger.logger.debug(s"handler [$deadboltHandler] :: subject is present - denying")
          false
        }
      }
    })(ec), timeoutInMillis)
  }

  /**
   * Used for restrict tags in the template.
   *
   * @param roles a list of String arrays.  Within an array, the roles are ANDed.  The arrays in the list are OR'd, so
   *              the first positive hit will allow access.
   * @param deadboltHandler application hook
   * @return true if the view can be accessed, otherwise false
   */
  def restrict(roles: List[Array[String]],
               deadboltHandler: DeadboltHandler,
               timeoutInMillis: Long,
               request: Request[Any]): Boolean = {
    def check(analyzer: DeadboltAnalyzer, subject: Optional[Subject], current: Array[String], remaining: List[Array[String]]): Boolean = {
        if (analyzer.checkRole(subject, current)) true
        else if (remaining.isEmpty) false
        else check(analyzer, subject, remaining.head, remaining.tail)
    }

    tryToComplete(deadboltHandler.getSubject(request).map((subjectOption: Option[Subject]) => {
      subjectOption match {
        case Some(subject) => check(new DeadboltAnalyzer(), Optional.ofNullable(subject), roles.head, roles.tail)
        case None => false
      }
    })(ec), timeoutInMillis)
  }

  /**
   * Used for dynamic tags in the template.
   *
   * @param name the name of the resource
   * @param meta meta information on the resource
   * @return true if the view can be accessed, otherwise false
   */
  def dynamic(name: String,
              meta: String,
              deadboltHandler: DeadboltHandler,
              timeoutInMillis: Long,
              request: Request[Any]): Boolean = {
    tryToComplete(deadboltHandler.getDynamicResourceHandler(request).flatMap((drhOption: Option[DynamicResourceHandler]) => {
      drhOption match {
        case Some(drh) => drh.isAllowed(name, meta, deadboltHandler, request)
        case None => throw new RuntimeException("A dynamic resource is specified but no dynamic resource handler is provided")
      }
    })(ec), timeoutInMillis)
  }

  /**
   *
   * @param value the value of the pattern, e.g. the regex
   * @param patternType the type of pattern
   * @param deadboltHandler the handler to use for this request
   * @param request the request
   * @return
   */
  def pattern(value: String,
              patternType: PatternType,
              deadboltHandler: DeadboltHandler,
              timeoutInMillis: Long,
              request: Request[Any]): Boolean = {

    tryToComplete(deadboltHandler.getSubject(request).map((subjectOption: Option[Subject]) => {
      subjectOption match {
        case None => false
        case Some(subject) => patternType match {
          case PatternType.EQUALITY => new DeadboltAnalyzer().checkPatternEquality(Optional.ofNullable(subject), Optional.ofNullable(value))
          case PatternType.REGEX => new DeadboltAnalyzer().checkRegexPattern(Optional.ofNullable(subject), Optional.ofNullable(patternCache(value).orNull))
          case PatternType.CUSTOM =>
            val future: Future[Boolean] = deadboltHandler.getDynamicResourceHandler(request).map((drhOption: Option[DynamicResourceHandler]) => {
              drhOption match {
                case Some(drh) =>
                  Await.result(drh.checkPermission(value, deadboltHandler, request), timeoutInMillis milliseconds)
                case None =>
                  logger.error("A custom pattern is specified but no dynamic resource handler is provided")
                  throw new scala.RuntimeException("A custom pattern is specified but no dynamic resource handler is provided")
              }
            })(ec)
            Await.result(future, timeoutInMillis milliseconds)
          case _ => false
        }
      }
    })(ec), timeoutInMillis)
  }

  /**
   * Attempts to complete the future within the given number of milliseconds.
   *
   * @param future the future to complete
   * @param timeoutInMillis the number of milliseconds to wait for a result
   * @return false if the future times out, otherwise the result of the future
   */
  private def tryToComplete(future: Future[Boolean], timeoutInMillis: Long): Boolean =
    Try(Await.result(future, timeoutInMillis milliseconds)) match {
      case Success(allowed) => allowed
      case Failure(ex) =>
        logger.error("Error when checking view constraint", ex)
        listener.failure(s"Error when checking view constraint: [${ex.getMessage}]",
                         timeout)
        false
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy