Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
81db042
Phase 1: build → Scala 3.8.3 / Play 3.0.10 / Pekko 1.6
mathieuancelin Jun 12, 2026
87a6ecb
Phase 2: mechanical akka → org.apache.pekko rename (app/test/conf)
mathieuancelin Jun 12, 2026
182614f
Phase 3: forced rewrites (kaleidoscope, runtime reflection, swagger)
mathieuancelin Jun 12, 2026
705e739
Phase 4 (1/n): Scala 3 migration mode, keyword backticks, explicit im…
mathieuancelin Jun 12, 2026
4897d3d
Phase 4 (2/n): Play 3 ActionBuilder ambiguity + Scala 2.13 collection…
mathieuancelin Jun 12, 2026
a0b3be9
Phase 4 (3/n): netty implicits, mapValues brace-form, asScala convert…
mathieuancelin Jun 12, 2026
2822334
Phase 4 (4/n): MutableList→ListBuffer, reactor Consumer, ActionBuilde…
mathieuancelin Jun 12, 2026
ea6c929
Phase 4 (5/n): treerouter routes.toSeq + getOrElse(Seq.empty).toSeq
mathieuancelin Jun 12, 2026
71b5469
Phase 4 (6/n): CollectionConverters wildcard, r-shadowing, println au…
mathieuancelin Jun 12, 2026
9c817d8
Phase 4 (7/n): scala.jdk single-name imports, println tuples, globalC…
mathieuancelin Jun 12, 2026
fa27d11
Phase 4 (8/n): graphql JsNumber(Float).toDouble, sliceArrayWithArgs c…
mathieuancelin Jun 12, 2026
0f65841
Phase 4 (9/n): rediscala (using actorSystem), JsArray.value.toSeq at …
mathieuancelin Jun 12, 2026
c234a14
Phase 4 (10/n): doc.scala toSeq, WSCookie safeCookies.toSeq, webhooks…
mathieuancelin Jun 12, 2026
7d84ae6
Phase 4 (11/n): explicit type on implicit val logger = Logger(...) (f…
mathieuancelin Jun 12, 2026
b3e7883
Phase 4 (12/n): rediscala (using actorSystem) constructors, reactivep…
mathieuancelin Jun 12, 2026
9d65f5c
Phase 4 (13/n): export backtick (stateexporter), PekkoHttpServerCompo…
mathieuancelin Jun 12, 2026
d1210c8
Phase 4 (14/n): jwt remove unused parallel import, CustomCollector co…
mathieuancelin Jun 12, 2026
525e700
Phase 4 (15/n): asEither collection.Seq return types, more arr.value/…
mathieuancelin Jun 12, 2026
0e1e75f
Phase 4 (16/n): KaleidoscopeShim use scala.util.matching.Regex (shado…
mathieuancelin Jun 12, 2026
0ee1bfe
Phase 4 (17/n): rediscala fork SentinelMonitoredRedisClient username …
mathieuancelin Jun 12, 2026
7270a43
Phase 4 (18/n): dyn.scala ssl-config adaptation (secure defaults), sl…
mathieuancelin Jun 12, 2026
166db31
Phase 4 (19/n): newTargets/greenrules toSeq, h1h2client drop ambiguou…
mathieuancelin Jun 12, 2026
508c16b
Phase 4 (20/n): h1h2client drop local JavaConverters, eureka body[Str…
mathieuancelin Jun 12, 2026
7b115c6
Phase 4 (21/n): Source[ByteString,Any] ascription for applyOn/applyOn…
mathieuancelin Jun 12, 2026
89fa363
Phase 4 (22/n): graphql resolve JsValue, tailscale ResponseReceiver a…
mathieuancelin Jun 12, 2026
d0c291c
Phase 4 (23/n): tailscale ResponseReceiver asInstanceOf cast
mathieuancelin Jun 12, 2026
8856874
Phase 4 (24/n): Play 3 WSRequest withDisableUrlEncoding/addCookies st…
mathieuancelin Jun 12, 2026
b965c39
Phase 4 (25/25): coraza directives toSeq — app/ COMPILES on Scala 3 /…
mathieuancelin Jun 12, 2026
0b0cbd8
cleanup
mathieuancelin Jun 13, 2026
f39d968
add migration handoff
mathieuancelin Jun 13, 2026
83c7c95
migrate tests
mathieuancelin Jun 13, 2026
7064f51
cleanup
mathieuancelin Jun 13, 2026
e7974a9
update migration guide
mathieuancelin Jun 13, 2026
291401a
cleanup
mathieuancelin Jun 15, 2026
8c3009b
fix #2596
mathieuancelin Jun 15, 2026
a4a0e42
fix #2597
mathieuancelin Jun 15, 2026
1d61db8
fix #2598
mathieuancelin Jun 16, 2026
03d33b8
remove log
mathieuancelin Jun 17, 2026
89637d5
Increase sbt heap size to avoid heap space errors at compilation
ptitFicus Jun 18, 2026
d66201e
read audiance as array (fix #2607)
mathieuancelin Jun 22, 2026
16eaf64
fix #2608
mathieuancelin Jun 22, 2026
917d7a7
fix weird scrolling behaviour
Zwiterrion Jun 22, 2026
bf50c1b
completly remove ace
Zwiterrion Jun 23, 2026
21e9ba0
fix: report declared legacy quotas on apikey quotas endpoint
Zwiterrion Jun 23, 2026
27c5288
fix: stop sending removed _type to elasticsearch 8 on writes
Zwiterrion Jun 23, 2026
dda0c0d
add a button to fill es cluster version - fix #2614
mathieuancelin Jun 29, 2026
a8a0c26
add alert on error
mathieuancelin Jun 30, 2026
5db7c7a
adapt ElasticWritesAnalyticsSpec to Scala 3 ScalaTest API
mathieuancelin Jul 2, 2026
5cf6356
update js deps
mathieuancelin Jul 2, 2026
9fdea33
Merge origin/master (release 17.17.0) into scala3-play3-pekko-migration
mathieuancelin Jul 3, 2026
35d7dd2
switch implicit to using/given syntax
mathieuancelin Jul 3, 2026
43fbcfe
fix promise apply() syntax
mathieuancelin Jul 3, 2026
91ad3bc
fix java converters imports with scala.jdk.CollectionConverters.*
mathieuancelin Jul 3, 2026
6dd949e
fix warnings
mathieuancelin Jul 3, 2026
79eefe5
fix warnings
mathieuancelin Jul 3, 2026
0d7a8bb
fix mapValues warnings
mathieuancelin Jul 3, 2026
5b05772
fix long2int conversion warnings
mathieuancelin Jul 3, 2026
ada5b12
fix warnings
mathieuancelin Jul 3, 2026
7894a23
fix warnings
mathieuancelin Jul 3, 2026
99d1940
fix warnings
mathieuancelin Jul 3, 2026
68c4a8b
fix warnings
mathieuancelin Jul 6, 2026
563f3b2
fix warnings
mathieuancelin Jul 6, 2026
cc972fe
fix warnings
mathieuancelin Jul 6, 2026
764dcb5
fix warnings
mathieuancelin Jul 6, 2026
7ff8716
fix: retry ES template creation when the initial apply fails
Zwiterrion Jul 7, 2026
4b95eb9
fix warnings
mathieuancelin Jul 7, 2026
28bd127
fix warnings
mathieuancelin Jul 7, 2026
ae2689e
fix warnings
mathieuancelin Jul 7, 2026
e056a0d
fix warnings
mathieuancelin Jul 7, 2026
1548331
fix warnings
mathieuancelin Jul 7, 2026
d95c67b
cleanup
mathieuancelin Jul 7, 2026
c85fe56
update compiler flags
mathieuancelin Jul 8, 2026
df034e3
replace _ imports
mathieuancelin Jul 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 11 additions & 9 deletions otoroshi/app/OtoroshiLoader.scala
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
package otoroshi.loader

import com.softwaremill.macwire._
import com.softwaremill.macwire.*
import otoroshi.controllers.adminapi.InfosApiController
import controllers.{Assets, AssetsComponents}
import otoroshi.netty.ReactorNettyServer
import otoroshi.actions._
import otoroshi.actions.*
import otoroshi.api.OtoroshiLoaderHelper.EnvContainer
import otoroshi.api.{DocAction, GenericApiController, OtoroshiEnvHolder, OtoroshiLoaderHelper}
import otoroshi.controllers._
import otoroshi.controllers.adminapi._
import otoroshi.controllers.*
import otoroshi.controllers.adminapi.*
import otoroshi.env.Env
import otoroshi.gateway._
import otoroshi.loader.modules._
import otoroshi.gateway.*
import otoroshi.loader.modules.*
import otoroshi.next.controllers.{NgPluginsController, TryItController}
import otoroshi.next.controllers.adminapi._
import otoroshi.next.controllers.adminapi.*
import otoroshi.next.tunnel.TunnelController
import otoroshi.next.workflow.WorkflowsController
import play.api.ApplicationLoader.Context
import play.api.http.{DefaultHttpFilters, HttpErrorHandler, HttpRequestHandler}
import play.api.http.{CookiesConfiguration, DefaultHttpFilters, HttpErrorHandler, HttpRequestHandler}
import play.api.libs.ws.ahc.AhcWSComponents
import play.api.mvc.EssentialFilter
import play.api.mvc.{DefaultCookieHeaderEncoding, EssentialFilter}
import play.api.routing.Router
import play.api.{Application, ApplicationLoader, BuiltInComponentsFromContext, LoggerConfigurator}
import play.filters.HttpFiltersComponents
Expand Down Expand Up @@ -67,6 +67,8 @@ package object modules {

// lazy val gzipFilterConfig = GzipFilterConfig.fromConfiguration(configuration)
// lazy val gzipFilter = wire[GzipFilter]
lazy val headerEncoding = new play.api.mvc.DefaultCookieHeaderEncoding(httpConfiguration.cookies)

override lazy val httpFilters: Seq[EssentialFilter] = Seq()

lazy val circuitBreakersHolder: CircuitBreakersHolder = wire[CircuitBreakersHolder]
Expand Down
71 changes: 35 additions & 36 deletions otoroshi/app/actions/api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ package otoroshi.actions

import java.util.Base64
import java.util.concurrent.atomic.AtomicReference
import akka.http.scaladsl.util.FastFuture
import org.apache.pekko.http.scaladsl.util.FastFuture
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.google.common.base.Charsets
import java.nio.charset.StandardCharsets
import next.models.Api
import otoroshi.env.Env
import otoroshi.gateway.Errors
import otoroshi.models.{ApiKey, BackOfficeUser, EntityLocationSupport, _}
import otoroshi.models.RightsChecker.{SuperAdminOnly, TenantAdminOnly}
import otoroshi.utils.syntax.implicits._
import otoroshi.utils.syntax.implicits.*
import play.api.Logger
import play.api.libs.json.{JsArray, JsValue, Json}
import play.api.mvc._
import play.api.mvc.*
import otoroshi.security.{IdGenerator, OtoroshiClaim}
import otoroshi.utils.{JsonPathValidator, JsonValidator}
import otoroshi.utils.http.RequestImplicits._
import otoroshi.utils.http.RequestImplicits.*

import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}
Expand All @@ -35,12 +35,12 @@ trait ApiActionContextCapable {
lazy val forbidden = ApiActionContext.forbidden
lazy val fforbidden = ApiActionContext.fforbidden

def user(implicit env: Env): Option[JsValue] =
def user(using env: Env): Option[JsValue] =
request.headers
.get(env.Headers.OtoroshiAdminProfile)
.flatMap(p => Try(Json.parse(new String(Base64.getDecoder.decode(p), Charsets.UTF_8))).toOption)
.flatMap(p => Try(Json.parse(new String(Base64.getDecoder.decode(p), StandardCharsets.UTF_8))).toOption)

def from(implicit env: Env): String = request.theIpAddress
def from(using env: Env): String = request.theIpAddress

def ua: String = request.theUserAgent

Expand All @@ -50,7 +50,7 @@ trait ApiActionContextCapable {

private val bouRef = new AtomicReference[Either[String, Option[BackOfficeUser]]]()

def userIsSuperAdmin(implicit env: Env): Boolean = {
def userIsSuperAdmin(using env: Env): Boolean = {
backOfficeUser match {
case Left(_) => false
case Right(None) => {
Expand All @@ -68,13 +68,13 @@ trait ApiActionContextCapable {
}
}

def oneAuthorizedTenant(implicit env: Env): TenantId =
def oneAuthorizedTenant(using env: Env): TenantId =
backOfficeUser.toOption.flatten.map(_.rights.oneAuthorizedTenant).getOrElse(TenantId.default)

def oneAuthorizedTeam(implicit env: Env): TeamId =
def oneAuthorizedTeam(using env: Env): TeamId =
backOfficeUser.toOption.flatten.map(_.rights.oneAuthorizedTeam).getOrElse(TeamId.default)

def backOfficeUser(implicit env: Env): Either[String, Option[BackOfficeUser]] = {
def backOfficeUser(using env: Env): Either[String, Option[BackOfficeUser]] = {
Option(bouRef.get()).getOrElse {
(
if (env.bypassUserRightsCheck) {
Expand Down Expand Up @@ -104,7 +104,6 @@ trait ApiActionContextCapable {
adminEntityValidators = Map.empty
)
Right(user.some)
case _ => Left("You're not authorized here (invalid setup) ! ")
}
case Some(userJwt) =>
Try(JWT.require(Algorithm.HMAC512(apiKey.clientSecret)).build().verify(userJwt)) match {
Expand All @@ -128,19 +127,19 @@ trait ApiActionContextCapable {
}
}

private def rootOrTenantAdmin(user: BackOfficeUser)(f: => Boolean)(implicit env: Env): Boolean = {
private def rootOrTenantAdmin(user: BackOfficeUser)(f: => Boolean)(using env: Env): Boolean = {
if (env.bypassUserRightsCheck || SuperAdminOnly.canPerform(user, currentTenant)) { // || TenantAdminOnly.canPerform(user, currentTenant)) {
true
} else {
f
}
}

def canUserReadJson(item: JsValue)(implicit env: Env): Boolean = {
def canUserReadJson(item: JsValue)(using env: Env): Boolean = {
canUserRead(FakeEntityLocationSupport(EntityLocation.readFromKey(item)))
}

def canUserRead[T <: EntityLocationSupport](item: T)(implicit env: Env): Boolean = {
def canUserRead[T <: EntityLocationSupport](item: T)(using env: Env): Boolean = {
backOfficeUser match {
case Left(_) => false
case Right(None) => true
Expand All @@ -157,11 +156,11 @@ trait ApiActionContextCapable {
}
}

def canUserWriteJson(item: JsValue)(implicit env: Env): Boolean = {
def canUserWriteJson(item: JsValue)(using env: Env): Boolean = {
canUserWrite(FakeEntityLocationSupport(EntityLocation.readFromKey(item)))
}

def canUserWrite[T <: EntityLocationSupport](item: T)(implicit env: Env): Boolean = {
def canUserWrite[T <: EntityLocationSupport](item: T)(using env: Env): Boolean = {
backOfficeUser match {
case Left(_) => false
case Right(None) => true
Expand All @@ -179,7 +178,7 @@ trait ApiActionContextCapable {
}
}

def checkRights(rc: RightsChecker)(f: Future[Result])(implicit ec: ExecutionContext, env: Env): Future[Result] = {
def checkRights(rc: RightsChecker)(f: Future[Result])(using ec: ExecutionContext, env: Env): Future[Result] = {
if (env.bypassUserRightsCheck) {
f
} else {
Expand All @@ -198,7 +197,7 @@ trait ApiActionContextCapable {

private def findServiceById(
serviceId: String
)(implicit ec: ExecutionContext, env: Env): Future[Option[ServiceDescriptor]] = {
)(using ec: ExecutionContext, env: Env): Future[Option[ServiceDescriptor]] = {
def getRouteCompositions = {
env.datastores.routeCompositionDataStore.findById(serviceId) flatMap {
case Some(service) => service.toRoutes.head.legacy.some.vfuture
Expand Down Expand Up @@ -245,7 +244,7 @@ trait ApiActionContextCapable {
}

/// utils methods
def canReadService(id: String)(f: => Future[Result])(implicit ec: ExecutionContext, env: Env): Future[Result] = {
def canReadService(id: String)(f: => Future[Result])(using ec: ExecutionContext, env: Env): Future[Result] = {
if (id == "global") {
f
} else {
Expand All @@ -256,50 +255,50 @@ trait ApiActionContextCapable {
}
}

def canWriteService(id: String)(f: => Future[Result])(implicit ec: ExecutionContext, env: Env): Future[Result] = {
def canWriteService(id: String)(f: => Future[Result])(using ec: ExecutionContext, env: Env): Future[Result] = {
findServiceById(id).flatMap {
case Some(service) if canUserWrite(service) => f
case _ => fforbidden
}
}

def canReadApikey(id: String)(f: => Future[Result])(implicit ec: ExecutionContext, env: Env): Future[Result] = {
def canReadApikey(id: String)(f: => Future[Result])(using ec: ExecutionContext, env: Env): Future[Result] = {
env.datastores.apiKeyDataStore.findById(id).flatMap {
case Some(service) if canUserRead(service) => f
case _ => fforbidden
}
}

def canWriteApikey(id: String)(f: => Future[Result])(implicit ec: ExecutionContext, env: Env): Future[Result] = {
def canWriteApikey(id: String)(f: => Future[Result])(using ec: ExecutionContext, env: Env): Future[Result] = {
env.datastores.apiKeyDataStore.findById(id).flatMap {
case Some(service) if canUserWrite(service) => f
case _ => fforbidden
}
}

def canReadGroup(id: String)(f: => Future[Result])(implicit ec: ExecutionContext, env: Env): Future[Result] = {
def canReadGroup(id: String)(f: => Future[Result])(using ec: ExecutionContext, env: Env): Future[Result] = {
env.datastores.serviceGroupDataStore.findById(id).flatMap {
case Some(service) if canUserRead(service) => f
case _ => fforbidden
}
}

def canWriteGroup(id: String)(f: => Future[Result])(implicit ec: ExecutionContext, env: Env): Future[Result] = {
def canWriteGroup(id: String)(f: => Future[Result])(using ec: ExecutionContext, env: Env): Future[Result] = {
env.datastores.serviceGroupDataStore.findById(id).flatMap {
case Some(service) if canUserWrite(service) => f
case _ => fforbidden
}
}

def canWriteAuthModule(id: String)(f: => Future[Result])(implicit ec: ExecutionContext, env: Env): Future[Result] = {
def canWriteAuthModule(id: String)(f: => Future[Result])(using ec: ExecutionContext, env: Env): Future[Result] = {
// env.datastores.authConfigsDataStore.findById(id).flatMap {
env.proxyState.authModuleAsync(id).flatMap {
case Some(mod) if canUserWrite(mod) => f
case _ => fforbidden
}
}

def validateEntity(json: JsValue, singularName: String)(implicit env: Env): Either[JsValue, JsValue] = {
def validateEntity(json: JsValue, singularName: String)(using env: Env): Either[JsValue, JsValue] = {
backOfficeUser match {
case Left(err) => Left(err.json)
case Right(None) => Right(json)
Expand Down Expand Up @@ -329,17 +328,17 @@ trait ApiActionContextCapable {

case class ApiActionContext[A](apiKey: ApiKey, request: Request[A]) extends ApiActionContextCapable {}

class ApiAction(val parser: BodyParser[AnyContent])(implicit env: Env)
class ApiAction(val parser: BodyParser[AnyContent])(using env: Env)
extends ActionBuilder[ApiActionContext, AnyContent]
with ActionFunction[Request, ApiActionContext] {

implicit lazy val ec = env.otoroshiExecutionContext
implicit lazy val ec: scala.concurrent.ExecutionContext = env.otoroshiExecutionContext

lazy val logger = Logger("otoroshi-api-action")

def decodeBase64(encoded: String): String = new String(OtoroshiClaim.decoder.decode(encoded), Charsets.UTF_8)
def decodeBase64(encoded: String): String = new String(OtoroshiClaim.decoder.decode(encoded), StandardCharsets.UTF_8)

def error(message: String, ex: Option[Throwable] = None)(implicit request: Request[_]): Future[Result] = {
def error(message: String, ex: Option[Throwable] = None)(using request: Request[_]): Future[Result] = {
ex match {
case Some(e) => logger.error(s"error message: $message", e)
case None => logger.error(s"error message: $message")
Expand All @@ -361,7 +360,7 @@ class ApiAction(val parser: BodyParser[AnyContent])(implicit env: Env)
def perform(): Future[Result] = {
request.headers.get(env.Headers.OtoroshiClaim).get.split("\\.").toSeq match {
case Seq(head, body, signature) => {
val claim = Json.parse(new String(OtoroshiClaim.decoder.decode(body), Charsets.UTF_8))
val claim = Json.parse(new String(OtoroshiClaim.decoder.decode(body), StandardCharsets.UTF_8))
val lastestApikey = (claim \ "access_type").asOpt[String].exists(v => v == "apikey" || v == "both")
val latestClientId = (claim \ "apikey" \ "clientId").asOpt[String]
(claim \ "sub").as[String].split(":").toSeq match {
Expand Down Expand Up @@ -439,15 +438,15 @@ class ApiAction(val parser: BodyParser[AnyContent])(implicit env: Env)

case class UnAuthApiActionContent[A](req: Request[A])

class UnAuthApiAction(val parser: BodyParser[AnyContent])(implicit env: Env)
class UnAuthApiAction(val parser: BodyParser[AnyContent])(using env: Env)
extends ActionBuilder[UnAuthApiActionContent, AnyContent]
with ActionFunction[Request, UnAuthApiActionContent] {

implicit lazy val ec = env.otoroshiExecutionContext
implicit lazy val ec: scala.concurrent.ExecutionContext = env.otoroshiExecutionContext

lazy val logger = Logger("otoroshi-api-action")

def error(message: String, ex: Option[Throwable] = None)(implicit request: Request[_]): Future[Result] = {
def error(message: String, ex: Option[Throwable] = None)(using request: Request[_]): Future[Result] = {
ex match {
case Some(e) => logger.error(s"error message: $message", e)
case None => logger.error(s"error message: $message")
Expand Down
32 changes: 16 additions & 16 deletions otoroshi/app/actions/backoffice.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package otoroshi.actions

import akka.http.scaladsl.model.Uri
import akka.http.scaladsl.model.Uri.Path
import akka.http.scaladsl.util.FastFuture
import org.apache.pekko.http.scaladsl.model.Uri
import org.apache.pekko.http.scaladsl.model.Uri.Path
import org.apache.pekko.http.scaladsl.util.FastFuture
import otoroshi.auth.GenericOauth2Module
import otoroshi.env.Env
import otoroshi.events.{Alerts, BlackListedBackOfficeUserAlert}
Expand All @@ -11,17 +11,17 @@ import otoroshi.models.BackOfficeUser
import otoroshi.models.RightsChecker.{SuperAdminOnly, TenantAdminOnly}
import otoroshi.models.{EntityLocationSupport, RightsChecker, TenantId}
import otoroshi.utils.TypedMap
import otoroshi.utils.syntax.implicits._
import otoroshi.utils.syntax.implicits.*
import play.api.libs.json.Json
import play.api.mvc.Results.Status
import play.api.mvc._
import otoroshi.utils.http.RequestImplicits._
import play.api.mvc.*
import otoroshi.utils.http.RequestImplicits.*

import scala.concurrent.{ExecutionContext, Future}

case class BackOfficeActionContext[A](request: Request[A], user: Option[BackOfficeUser]) {
def connected: Boolean = user.isDefined
def from(implicit env: Env): String = request.theIpAddress
def from(using env: Env): String = request.theIpAddress
def ua: String = request.theUserAgent
}

Expand All @@ -30,36 +30,36 @@ case class BackOfficeActionContextAuth[A](request: Request[A], user: BackOfficeU
lazy val forbidden = ApiActionContext.forbidden
lazy val fforbidden = ApiActionContext.fforbidden

def from(implicit env: Env): String = request.theIpAddress
def from(using env: Env): String = request.theIpAddress
def ua: String = request.theUserAgent

lazy val currentTenant: TenantId = {
val value = request.headers.get("Otoroshi-Tenant").getOrElse("default")
TenantId(value)
}

private def rootOrTenantAdmin(user: BackOfficeUser)(f: => Boolean)(implicit env: Env): Boolean = {
private def rootOrTenantAdmin(user: BackOfficeUser)(f: => Boolean)(using env: Env): Boolean = {
if (env.bypassUserRightsCheck || SuperAdminOnly.canPerform(user, currentTenant)) { // || TenantAdminOnly.canPerform(user, currentTenant)) {
true
} else {
f
}
}

def canUserRead[T <: EntityLocationSupport](item: T)(implicit env: Env): Boolean = {
def canUserRead[T <: EntityLocationSupport](item: T)(using env: Env): Boolean = {
rootOrTenantAdmin(user) {
(currentTenant.value == item.location.tenant.value || item.location.tenant == TenantId.all) && user.rights
.canReadTenant(item.location.tenant) && user.rights.canReadTeams(currentTenant, item.location.teams)
}
}
def canUserWrite[T <: EntityLocationSupport](item: T)(implicit env: Env): Boolean = {
def canUserWrite[T <: EntityLocationSupport](item: T)(using env: Env): Boolean = {
rootOrTenantAdmin(user) {
(currentTenant.value == item.location.tenant.value || item.location.tenant == TenantId.all) && user.rights
.canWriteTenant(item.location.tenant) && user.rights.canWriteTeams(currentTenant, item.location.teams)
}
}

def checkRights(rc: RightsChecker)(f: Future[Result])(implicit ec: ExecutionContext, env: Env): Future[Result] = {
def checkRights(rc: RightsChecker)(f: Future[Result])(using ec: ExecutionContext, env: Env): Future[Result] = {
if (env.bypassUserRightsCheck) {
f
} else {
Expand All @@ -72,11 +72,11 @@ case class BackOfficeActionContextAuth[A](request: Request[A], user: BackOfficeU
}
}

class BackOfficeAction(val parser: BodyParser[AnyContent])(implicit env: Env)
class BackOfficeAction(val parser: BodyParser[AnyContent])(using env: Env)
extends ActionBuilder[BackOfficeActionContext, AnyContent]
with ActionFunction[Request, BackOfficeActionContext] {

implicit lazy val ec = env.otoroshiExecutionContext
implicit lazy val ec: scala.concurrent.ExecutionContext = env.otoroshiExecutionContext

override def invokeBlock[A](
request: Request[A],
Expand Down Expand Up @@ -112,11 +112,11 @@ class BackOfficeAction(val parser: BodyParser[AnyContent])(implicit env: Env)
override protected def executionContext: ExecutionContext = ec
}

class BackOfficeActionAuth(val parser: BodyParser[AnyContent])(implicit env: Env)
class BackOfficeActionAuth(val parser: BodyParser[AnyContent])(using env: Env)
extends ActionBuilder[BackOfficeActionContextAuth, AnyContent]
with ActionFunction[Request, BackOfficeActionContextAuth] {

implicit lazy val ec = env.otoroshiExecutionContext
implicit lazy val ec: scala.concurrent.ExecutionContext = env.otoroshiExecutionContext

// val checker = new AdminClearanceChecker()(env)

Expand Down
Loading
Loading