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

next.plugins.paths.scala Maven / Gradle / Ivy

package otoroshi.next.plugins

import otoroshi.env.Env
import otoroshi.gateway.Errors
import otoroshi.next.plugins.api.{
  NgAccess,
  NgAccessContext,
  NgAccessValidator,
  NgPluginCategory,
  NgPluginConfig,
  NgPluginVisibility,
  NgStep
}
import otoroshi.utils.http.RequestImplicits._
import otoroshi.utils.syntax.implicits._
import play.api.libs.json._
import play.api.mvc.Results

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

case class NgPublicPrivatePathsConfig(
    strict: Boolean = false,
    publicPatterns: Seq[String] = Seq.empty,
    privatePatterns: Seq[String] = Seq.empty
) extends NgPluginConfig {
  def json: JsValue = NgPublicPrivatePathsConfig.format.writes(this)
}

object NgPublicPrivatePathsConfig {
  val format = new Format[NgPublicPrivatePathsConfig] {
    override def reads(json: JsValue): JsResult[NgPublicPrivatePathsConfig] = Try {
      NgPublicPrivatePathsConfig(
        privatePatterns = json.select("private_patterns").asOpt[Seq[String]].getOrElse(Seq.empty),
        publicPatterns = json.select("public_patterns").asOpt[Seq[String]].getOrElse(Seq.empty),
        strict = json.select("strict").asOpt[Boolean].getOrElse(false)
      )
    } match {
      case Failure(e) => JsError(e.getMessage)
      case Success(c) => JsSuccess(c)
    }
    override def writes(o: NgPublicPrivatePathsConfig): JsValue             = Json.obj(
      "strict"           -> o.strict,
      "private_patterns" -> o.privatePatterns,
      "public_patterns"  -> o.publicPatterns
    )
  }
}

class PublicPrivatePaths extends NgAccessValidator {

  private val configReads: Reads[NgPublicPrivatePathsConfig] = NgPublicPrivatePathsConfig.format

  override def steps: Seq[NgStep]                = Seq(NgStep.ValidateAccess)
  override def categories: Seq[NgPluginCategory] = Seq(NgPluginCategory.AccessControl)
  override def visibility: NgPluginVisibility    = NgPluginVisibility.NgUserLand

  override def multiInstance: Boolean                      = true
  override def core: Boolean                               = true
  override def name: String                                = "Public/Private paths"
  override def description: Option[String]                 = "This plugin allows or forbid request based on path patterns".some
  override def defaultConfigObject: Option[NgPluginConfig] = NgPublicPrivatePathsConfig().some
  override def isAccessAsync: Boolean                      = true

  override def access(ctx: NgAccessContext)(implicit env: Env, ec: ExecutionContext): Future[NgAccess] = {
    val uri                                                                 = ctx.request.thePath
    val NgPublicPrivatePathsConfig(strict, publicPatterns, privatePatterns) =
      ctx.cachedConfig(internalName)(configReads).getOrElse(NgPublicPrivatePathsConfig())
    val isPublic                                                            =
      !privatePatterns.exists(p => otoroshi.utils.RegexPool.regex(p).matches(uri)) && publicPatterns.exists(p =>
        otoroshi.utils.RegexPool.regex(p).matches(uri)
      )
    if (isPublic) {
      NgAccess.NgAllowed.vfuture
    } else if (!isPublic && !strict && (ctx.apikey.isDefined || ctx.user.isDefined)) {
      NgAccess.NgAllowed.vfuture
    } else if (!isPublic && strict && ctx.apikey.isDefined) {
      NgAccess.NgAllowed.vfuture
    } else {
      Errors
        .craftResponseResult(
          "Not authorized",
          Results.Unauthorized,
          ctx.request,
          None,
          Some("errors.unauthorized"),
          attrs = ctx.attrs,
          maybeRoute = ctx.route.some
        )
        .map(NgAccess.NgDenied.apply)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy