diff --git a/otoroshi/app/models/descriptor.scala b/otoroshi/app/models/descriptor.scala index b0797eb734..a10a2dedd2 100644 --- a/otoroshi/app/models/descriptor.scala +++ b/otoroshi/app/models/descriptor.scala @@ -204,7 +204,7 @@ object LoadBalancing { case Some("BestResponseTime") => JsSuccess(BestResponseTime) case Some("WeightedBestResponseTime") => JsSuccess(WeightedBestResponseTime((json \ "ratio").asOpt[Double].getOrElse(0.5))) - case _ => JsSuccess(RoundRobin) + case _ => JsSuccess(RoundRobin) } } } diff --git a/otoroshi/app/next/controllers/entities.scala b/otoroshi/app/next/controllers/entities.scala index 311c03a02c..02fe03be0b 100644 --- a/otoroshi/app/next/controllers/entities.scala +++ b/otoroshi/app/next/controllers/entities.scala @@ -19,16 +19,25 @@ class EntitiesController(ApiAction: ApiAction, cc: ControllerComponents)(implici ApiAction.async { ctx => entity match { case "jwt-verifiers" => - Ok(Json.obj( - "routes" -> env.proxyState - .allRoutes() - .filter(route => ctx.canUserRead(route)) - .filter(route => Json.stringify(Json.arr(route.plugins.slots - .map(p => p.config.raw))).contains(id) - ) - .map(_.json) - )).as("application/json").vfuture - case _ => + Ok( + Json.obj( + "routes" -> env.proxyState + .allRoutes() + .filter(route => ctx.canUserRead(route)) + .filter(route => + Json + .stringify( + Json.arr( + route.plugins.slots + .map(p => p.config.raw) + ) + ) + .contains(id) + ) + .map(_.json) + ) + ).as("application/json").vfuture + case _ => Ok(Json.obj()).as("application/json").vfuture } } diff --git a/otoroshi/app/next/models/route.scala b/otoroshi/app/next/models/route.scala index 013094684f..16e7fff951 100644 --- a/otoroshi/app/next/models/route.scala +++ b/otoroshi/app/next/models/route.scala @@ -958,11 +958,13 @@ object NgRoute { .applyOn { seq => seq :+ NgPluginInstance( plugin = pluginId[NgLegacyApikeyCall], - config = NgPluginInstanceConfig(NgLegacyApikeyCallConfig( - publicPatterns = service.publicPatterns, - privatePatterns = service.privatePatterns, - config = NgApikeyCallsConfig.fromLegacy(service.apiKeyConstraints) - ).json.asObject) + config = NgPluginInstanceConfig( + NgLegacyApikeyCallConfig( + publicPatterns = service.publicPatterns, + privatePatterns = service.privatePatterns, + config = NgApikeyCallsConfig.fromLegacy(service.apiKeyConstraints) + ).json.asObject + ) ) } //.applyOnIf( @@ -1022,7 +1024,8 @@ object NgRoute { seq :+ NgPluginInstance( plugin = pluginId[AuthModule], include = if (service.publicPatterns.nonEmpty) { - if (service.publicPatterns.size == 1 && service.publicPatterns.contains("/.*")) Seq.empty else service.publicPatterns + if (service.publicPatterns.size == 1 && service.publicPatterns.contains("/.*")) Seq.empty + else service.publicPatterns } else { Seq.empty }, diff --git a/otoroshi/app/next/plugins/apikey.scala b/otoroshi/app/next/plugins/apikey.scala index cd123e3fec..3c2a07622c 100644 --- a/otoroshi/app/next/plugins/apikey.scala +++ b/otoroshi/app/next/plugins/apikey.scala @@ -14,15 +14,19 @@ import scala.concurrent.duration.DurationInt import scala.concurrent.{Await, ExecutionContext, Future} import scala.util.{Failure, Success, Try} -case class NgLegacyApikeyCallConfig(publicPatterns: Seq[String] = Seq.empty, privatePatterns: Seq[String] = Seq.empty, config: NgApikeyCallsConfig) extends NgPluginConfig { +case class NgLegacyApikeyCallConfig( + publicPatterns: Seq[String] = Seq.empty, + privatePatterns: Seq[String] = Seq.empty, + config: NgApikeyCallsConfig +) extends NgPluginConfig { override def json: JsValue = NgLegacyApikeyCallConfig.format.writes(this) } object NgLegacyApikeyCallConfig { val default = NgLegacyApikeyCallConfig(Seq.empty, Seq.empty, NgApikeyCallsConfig()) - val format = new Format[NgLegacyApikeyCallConfig] { - override def writes(o: NgLegacyApikeyCallConfig): JsValue = Json.obj( - "public_patterns" -> o.publicPatterns, - "private_patterns" -> o.privatePatterns, + val format = new Format[NgLegacyApikeyCallConfig] { + override def writes(o: NgLegacyApikeyCallConfig): JsValue = Json.obj( + "public_patterns" -> o.publicPatterns, + "private_patterns" -> o.privatePatterns ) ++ o.config.json.asObject override def reads(json: JsValue): JsResult[NgLegacyApikeyCallConfig] = Try { NgLegacyApikeyCallConfig( @@ -31,7 +35,7 @@ object NgLegacyApikeyCallConfig { config = NgApikeyCallsConfig.format.reads(json).asOpt.getOrElse(NgApikeyCallsConfig()) ) } match { - case Failure(e) => JsError(e.getMessage) + case Failure(e) => JsError(e.getMessage) case Success(value) => JsSuccess(value) } } @@ -60,24 +64,38 @@ class NgLegacyApikeyCall extends NgAccessValidator with NgRequestTransformer wit override def isTransformResponseAsync: Boolean = false override def isAccessAsync: Boolean = true override def name: String = "Legacy apikeys" - override def description: Option[String] = "This plugin expects to find an apikey to allow the request to pass. This plugin behaves exactly like the service descriptor does".some + override def description: Option[String] = + "This plugin expects to find an apikey to allow the request to pass. This plugin behaves exactly like the service descriptor does".some override def defaultConfigObject: Option[NgPluginConfig] = NgLegacyApikeyCallConfig.default.some override def matches(ctx: NgRouteMatcherContext)(implicit env: Env): Boolean = { - val plugin = env.scriptManager.getAnyScript[NgRouteMatcher](NgPluginHelper.pluginId[ApikeyCalls])(env.otoroshiExecutionContext).right.get + val plugin = env.scriptManager + .getAnyScript[NgRouteMatcher](NgPluginHelper.pluginId[ApikeyCalls])(env.otoroshiExecutionContext) + .right + .get plugin.matches(ctx)(env) } override def transformRequestSync( - ctx: NgTransformerRequestContext + ctx: NgTransformerRequestContext )(implicit env: Env, ec: ExecutionContext, mat: Materializer): Either[Result, NgPluginHttpRequest] = { - val plugin = env.scriptManager.getAnyScript[NgRequestTransformer](NgPluginHelper.pluginId[ApikeyCalls])(env.otoroshiExecutionContext).right.get + val plugin = env.scriptManager + .getAnyScript[NgRequestTransformer](NgPluginHelper.pluginId[ApikeyCalls])(env.otoroshiExecutionContext) + .right + .get plugin.transformRequestSync(ctx)(env, ec, mat) } override def access(ctx: NgAccessContext)(implicit env: Env, ec: ExecutionContext): Future[NgAccess] = { - val plugin = env.scriptManager.getAnyScript[NgAccessValidator](NgPluginHelper.pluginId[ApikeyCalls])(env.otoroshiExecutionContext).right.get - val config = configCache.get(ctx.route.cacheableId, _ => configReads.reads(ctx.config).getOrElse(NgLegacyApikeyCallConfig.default)) - val descriptor = ctx.route.legacy.copy(publicPatterns = config.publicPatterns, privatePatterns = config.privatePatterns) - val req = ctx.request + val plugin = env.scriptManager + .getAnyScript[NgAccessValidator](NgPluginHelper.pluginId[ApikeyCalls])(env.otoroshiExecutionContext) + .right + .get + val config = configCache.get( + ctx.route.cacheableId, + _ => configReads.reads(ctx.config).getOrElse(NgLegacyApikeyCallConfig.default) + ) + val descriptor = + ctx.route.legacy.copy(publicPatterns = config.publicPatterns, privatePatterns = config.privatePatterns) + val req = ctx.request if (descriptor.isUriPublic(req.path)) { if ( env.detectApiKeySooner && descriptor.detectApiKeySooner && ApiKeyHelper diff --git a/otoroshi/app/next/plugins/jwt.scala b/otoroshi/app/next/plugins/jwt.scala index fe803bdc58..d675086e45 100644 --- a/otoroshi/app/next/plugins/jwt.scala +++ b/otoroshi/app/next/plugins/jwt.scala @@ -57,11 +57,11 @@ class JwtVerification extends NgAccessValidator with NgRequestTransformer { override def defaultConfigObject: Option[NgPluginConfig] = NgJwtVerificationConfig().some override def access(ctx: NgAccessContext)(implicit env: Env, ec: ExecutionContext): Future[NgAccess] = { - val config = ctx.cachedConfig(internalName)(NgJwtVerificationConfig.format).getOrElse(NgJwtVerificationConfig()) + val config = ctx.cachedConfig(internalName)(NgJwtVerificationConfig.format).getOrElse(NgJwtVerificationConfig()) config.verifiers match { - case Nil => JwtVerifierUtils.onError() - case verifierIds => JwtVerifierUtils.verify(ctx, verifierIds) + case Nil => JwtVerifierUtils.onError() + case verifierIds => JwtVerifierUtils.verify(ctx, verifierIds) } } @@ -93,22 +93,28 @@ class JwtVerification extends NgAccessValidator with NgRequestTransformer { } object JwtVerifierUtils { - def onError = () => NgAccess.NgDenied( - Results.BadRequest(Json.obj("error" -> "bad request")) - ).vfuture + def onError = () => + NgAccess + .NgDenied( + Results.BadRequest(Json.obj("error" -> "bad request")) + ) + .vfuture - def verify(ctx: NgAccessContext, verifierIds: Seq[String]) - (implicit env: Env, ec: ExecutionContext): Future[NgAccess] = { + def verify(ctx: NgAccessContext, verifierIds: Seq[String])(implicit + env: Env, + ec: ExecutionContext + ): Future[NgAccess] = { val verifier = RefJwtVerifier(verifierIds, enabled = true, Seq.empty) if (verifier.isAsync) { - verifier.verifyFromCache( - request = ctx.request, - desc = ctx.route.serviceDescriptor.some, - apikey = ctx.apikey, - user = ctx.user, - elContext = ctx.attrs.get(otoroshi.plugins.Keys.ElCtxKey).getOrElse(Map.empty), - attrs = ctx.attrs - ) + verifier + .verifyFromCache( + request = ctx.request, + desc = ctx.route.serviceDescriptor.some, + apikey = ctx.apikey, + user = ctx.user, + elContext = ctx.attrs.get(otoroshi.plugins.Keys.ElCtxKey).getOrElse(Map.empty), + attrs = ctx.attrs + ) .flatMap { case Left(result) => onError() case Right(injection) => @@ -133,8 +139,8 @@ object JwtVerifierUtils { } } - -case class NgJwtVerificationOnlyConfig(verifier: Option[String] = None, failIfAbsent: Boolean = true) extends NgPluginConfig { +case class NgJwtVerificationOnlyConfig(verifier: Option[String] = None, failIfAbsent: Boolean = true) + extends NgPluginConfig { def json: JsValue = NgJwtVerificationOnlyConfig.format.writes(this) } @@ -150,7 +156,7 @@ object NgJwtVerificationOnlyConfig { case Success(c) => JsSuccess(c) } override def writes(o: NgJwtVerificationOnlyConfig): JsValue = Json.obj( - "verifier" -> o.verifier, + "verifier" -> o.verifier, "fail_if_absent" -> o.failIfAbsent ) } @@ -171,31 +177,32 @@ class JwtVerificationOnly extends NgAccessValidator with NgRequestTransformer { override def transformsResponse: Boolean = false override def transformsError: Boolean = false - override def isAccessAsync: Boolean = true - override def isTransformRequestAsync: Boolean = false - override def isTransformResponseAsync: Boolean = true - override def name: String = "Jwt verification only" - override def description: Option[String] = + override def isAccessAsync: Boolean = true + override def isTransformRequestAsync: Boolean = false + override def isTransformResponseAsync: Boolean = true + override def name: String = "Jwt verification only" + override def description: Option[String] = "This plugin verifies the current request with one jwt verifier".some override def access(ctx: NgAccessContext)(implicit env: Env, ec: ExecutionContext): Future[NgAccess] = { - val config = ctx.cachedConfig(internalName)(NgJwtVerificationOnlyConfig.format).getOrElse(NgJwtVerificationOnlyConfig()) + val config = + ctx.cachedConfig(internalName)(NgJwtVerificationOnlyConfig.format).getOrElse(NgJwtVerificationOnlyConfig()) if (config.failIfAbsent) { config.verifier match { - case None => JwtVerifierUtils.onError() + case None => JwtVerifierUtils.onError() case Some(verifierId) => env.proxyState.jwtVerifier(verifierId) match { - case None => JwtVerifierUtils.onError() + case None => JwtVerifierUtils.onError() case Some(verifier) => verifier.source.token(ctx.request) match { case Some(_) => config.verifier match { - case None => JwtVerifierUtils.onError() + case None => JwtVerifierUtils.onError() case Some(verifierId) => JwtVerifierUtils.verify(ctx, Seq(verifierId)) } NgAccess.NgAllowed.vfuture - case _ => JwtVerifierUtils.onError() + case _ => JwtVerifierUtils.onError() } } } @@ -205,7 +212,11 @@ class JwtVerificationOnly extends NgAccessValidator with NgRequestTransformer { } } -case class NgJwtSignerConfig(verifier: Option[String] = None, replaceIfPresent: Boolean = true, failIfPresent: Boolean = false) extends NgPluginConfig { +case class NgJwtSignerConfig( + verifier: Option[String] = None, + replaceIfPresent: Boolean = true, + failIfPresent: Boolean = false +) extends NgPluginConfig { def json: JsValue = NgJwtSignerConfig.format.writes(this) } @@ -215,16 +226,16 @@ object NgJwtSignerConfig { NgJwtSignerConfig( verifier = json.select("verifier").asOpt[String], replaceIfPresent = json.select("replace_if_present").asOpt[Boolean].getOrElse(true), - failIfPresent = json.select("fail_if_present").asOpt[Boolean].getOrElse(true), + failIfPresent = json.select("fail_if_present").asOpt[Boolean].getOrElse(true) ) } match { case Failure(e) => JsError(e.getMessage) case Success(c) => JsSuccess(c) } override def writes(o: NgJwtSignerConfig): JsValue = Json.obj( - "verifier" -> o.verifier, + "verifier" -> o.verifier, "replace_if_present" -> o.replaceIfPresent, - "fail_if_present" -> o.failIfPresent + "fail_if_present" -> o.failIfPresent ) } } @@ -244,25 +255,25 @@ class JwtSigner extends NgAccessValidator with NgRequestTransformer { override def transformsResponse: Boolean = false override def transformsError: Boolean = false - override def isAccessAsync: Boolean = true - override def isTransformRequestAsync: Boolean = false - override def isTransformResponseAsync: Boolean = true - override def name: String = "Jwt signer" - override def description: Option[String] = "This plugin can only generate token".some + override def isAccessAsync: Boolean = true + override def isTransformRequestAsync: Boolean = false + override def isTransformResponseAsync: Boolean = true + override def name: String = "Jwt signer" + override def description: Option[String] = "This plugin can only generate token".some override def access(ctx: NgAccessContext)(implicit env: Env, ec: ExecutionContext): Future[NgAccess] = { - val config = ctx.cachedConfig(internalName)(NgJwtSignerConfig.format).getOrElse(NgJwtSignerConfig()) + val config = ctx.cachedConfig(internalName)(NgJwtSignerConfig.format).getOrElse(NgJwtSignerConfig()) if (config.failIfPresent) { config.verifier match { - case None => NgAccess.NgAllowed.vfuture + case None => NgAccess.NgAllowed.vfuture case Some(verifierId) => env.proxyState.jwtVerifier(verifierId) match { - case None => NgAccess.NgAllowed.vfuture + case None => NgAccess.NgAllowed.vfuture case Some(verifier) => verifier.source.token(ctx.request) match { case None => NgAccess.NgDenied(Results.BadRequest(Json.obj("error" -> "bad request"))).vfuture - case _ => NgAccess.NgAllowed.vfuture + case _ => NgAccess.NgAllowed.vfuture } } } @@ -272,96 +283,107 @@ class JwtSigner extends NgAccessValidator with NgRequestTransformer { } override def transformRequestSync( - ctx: NgTransformerRequestContext - )(implicit env: Env, ec: ExecutionContext, mat: Materializer): Either[Result, NgPluginHttpRequest] = { - val config = ctx.cachedConfig(internalName)(NgJwtSignerConfig.format).getOrElse(NgJwtSignerConfig()) + ctx: NgTransformerRequestContext + )(implicit env: Env, ec: ExecutionContext, mat: Materializer): Either[Result, NgPluginHttpRequest] = { + val config = ctx.cachedConfig(internalName)(NgJwtSignerConfig.format).getOrElse(NgJwtSignerConfig()) config.verifier match { - case None => Results - .BadRequest(Json.obj("error" -> "bad request")) - .left + case None => + Results + .BadRequest(Json.obj("error" -> "bad request")) + .left case Some(verifierId) => env.proxyState.jwtVerifier(verifierId) match { - case None => Results + case None => + Results .BadRequest(Json.obj("error" -> "bad request")) .left - case Some(globalVerifier) => - if(!config.replaceIfPresent && globalVerifier.source.token(ctx.request).isDefined) { - ctx.otoroshiRequest.right - } else { - globalVerifier.algoSettings.asAlgorithm(OutputMode) match { - case None => - Results - .BadRequest(Json.obj("error" -> "bad request")) - .left - case Some(tokenSigningAlgorithm) => { - val user = ctx.user.orElse(ctx.attrs.get(otoroshi.plugins.Keys.UserKey)) - val apikey = ctx.attrs.get(otoroshi.plugins.Keys.ApiKeyKey) - val optSub: Option[String] = apikey.map(_.clientName).orElse(user.map(_.email)) - - val token = Json.obj( - "jti" -> IdGenerator.uuid, - "iat" -> System.currentTimeMillis(), - "nbf" -> System.currentTimeMillis(), - "iss" -> "Otoroshi", - "exp" -> (System.currentTimeMillis() + 60000L), - "sub" -> JsString(optSub.getOrElse("anonymous")), - "aud" -> "backend" + case Some(globalVerifier) => + if (!config.replaceIfPresent && globalVerifier.source.token(ctx.request).isDefined) { + ctx.otoroshiRequest.right + } else { + globalVerifier.algoSettings.asAlgorithm(OutputMode) match { + case None => + Results + .BadRequest(Json.obj("error" -> "bad request")) + .left + case Some(tokenSigningAlgorithm) => { + val user = ctx.user.orElse(ctx.attrs.get(otoroshi.plugins.Keys.UserKey)) + val apikey = ctx.attrs.get(otoroshi.plugins.Keys.ApiKeyKey) + val optSub: Option[String] = apikey.map(_.clientName).orElse(user.map(_.email)) + + val token = Json.obj( + "jti" -> IdGenerator.uuid, + "iat" -> System.currentTimeMillis(), + "nbf" -> System.currentTimeMillis(), + "iss" -> "Otoroshi", + "exp" -> (System.currentTimeMillis() + 60000L), + "sub" -> JsString(optSub.getOrElse("anonymous")), + "aud" -> "backend" + ) + + val headerJson = Json + .obj("alg" -> tokenSigningAlgorithm.getName, "typ" -> "JWT") + .applyOnWithOpt(globalVerifier.algoSettings.keyId)((h, id) => h ++ Json.obj("kid" -> id)) + val header = + ApacheBase64.encodeBase64URLSafeString(Json.stringify(headerJson).getBytes(StandardCharsets.UTF_8)) + val payload = + ApacheBase64.encodeBase64URLSafeString(Json.stringify(token).getBytes(StandardCharsets.UTF_8)) + val content = String.format("%s.%s", header, payload) + val signatureBytes = + tokenSigningAlgorithm.sign( + header.getBytes(StandardCharsets.UTF_8), + payload.getBytes(StandardCharsets.UTF_8) ) - - val headerJson = Json - .obj("alg" -> tokenSigningAlgorithm.getName, "typ" -> "JWT") - .applyOnWithOpt(globalVerifier.algoSettings.keyId)((h, id) => h ++ Json.obj("kid" -> id)) - val header = ApacheBase64.encodeBase64URLSafeString(Json.stringify(headerJson).getBytes(StandardCharsets.UTF_8)) - val payload = ApacheBase64.encodeBase64URLSafeString(Json.stringify(token).getBytes(StandardCharsets.UTF_8)) - val content = String.format("%s.%s", header, payload) - val signatureBytes = - tokenSigningAlgorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8)) - val signature = ApacheBase64.encodeBase64URLSafeString(signatureBytes) - - val signedToken = s"$content.$signature" - - val originalToken = JWT.decode(signedToken) - ctx.attrs.put(otoroshi.plugins.Keys.MatchedOutputTokenKey -> token) - - (globalVerifier.source match { - case _: InQueryParam => - globalVerifier.source.asJwtInjection(originalToken, signedToken).right[Result] - case InHeader(n, _) => - val inj = globalVerifier.source.asJwtInjection(originalToken, signedToken) - globalVerifier.source match { - case InHeader(nn, _) if nn == n => inj.right[Result] - case _ => inj.copy(removeHeaders = Seq(n)).right[Result] + val signature = ApacheBase64.encodeBase64URLSafeString(signatureBytes) + + val signedToken = s"$content.$signature" + + val originalToken = JWT.decode(signedToken) + ctx.attrs.put(otoroshi.plugins.Keys.MatchedOutputTokenKey -> token) + + (globalVerifier.source match { + case _: InQueryParam => + globalVerifier.source.asJwtInjection(originalToken, signedToken).right[Result] + case InHeader(n, _) => + val inj = globalVerifier.source.asJwtInjection(originalToken, signedToken) + globalVerifier.source match { + case InHeader(nn, _) if nn == n => inj.right[Result] + case _ => inj.copy(removeHeaders = Seq(n)).right[Result] + } + case InCookie(n) => + globalVerifier.source + .asJwtInjection(originalToken, signedToken) + .copy(removeCookies = Seq(n)) + .right[Result] + }) match { + case Left(result) => result.left + case Right(newValue) => + ctx.otoroshiRequest + .applyOnIf(newValue.removeCookies.nonEmpty) { req => + req.copy(cookies = req.cookies.filterNot(c => newValue.removeCookies.contains(c.name))) } - case InCookie(n) => - globalVerifier.source - .asJwtInjection(originalToken, signedToken) - .copy(removeCookies = Seq(n)) - .right[Result] - }) match { - case Left(result) => result.left - case Right(newValue) => - ctx.otoroshiRequest - .applyOnIf(newValue.removeCookies.nonEmpty) { req => - req.copy(cookies = req.cookies.filterNot(c => newValue.removeCookies.contains(c.name))) - } - .applyOnIf(newValue.removeHeaders.nonEmpty) { req => - req.copy(headers = - req.headers.filterNot(tuple => newValue.removeHeaders.map(_.toLowerCase).contains(tuple._1.toLowerCase)) + .applyOnIf(newValue.removeHeaders.nonEmpty) { req => + req.copy(headers = + req.headers.filterNot(tuple => + newValue.removeHeaders.map(_.toLowerCase).contains(tuple._1.toLowerCase) ) - } - .applyOnIf(newValue.additionalHeaders.nonEmpty) { req => - req.copy(headers = req.headers ++ newValue.additionalHeaders) - } - .applyOnIf(newValue.additionalCookies.nonEmpty) { req => - req.copy(cookies = req.cookies ++ newValue.additionalCookies.map(t => DefaultWSCookie(t._1, t._2))) - } - .right - } + ) + } + .applyOnIf(newValue.additionalHeaders.nonEmpty) { req => + req.copy(headers = req.headers ++ newValue.additionalHeaders) + } + .applyOnIf(newValue.additionalCookies.nonEmpty) { req => + req.copy(cookies = + req.cookies ++ newValue.additionalCookies.map(t => DefaultWSCookie(t._1, t._2)) + ) + } + .right } } } - } + } + } } } -} \ No newline at end of file +} diff --git a/otoroshi/app/utils/httpclient.scala b/otoroshi/app/utils/httpclient.scala index edd6de388e..3b15821154 100644 --- a/otoroshi/app/utils/httpclient.scala +++ b/otoroshi/app/utils/httpclient.scala @@ -1435,8 +1435,11 @@ case class AkkaWsClientRequest( .toList // ++ ua val onInternalApi = updatedHeaders.getIgnoreCase("Host").map(_.last).contains(env.adminApiHost) - val proto = targetOpt.map(_.protocol.asAkka).getOrElse(protocol) - val finalProtocol = if (proto == HttpProtocols.`HTTP/1.0` && onInternalApi) HttpProtocols.`HTTP/1.1` else (if (akkaHttpEntity.isChunked() && proto == HttpProtocols.`HTTP/1.0`) HttpProtocols.`HTTP/1.1` else proto) + val proto = targetOpt.map(_.protocol.asAkka).getOrElse(protocol) + val finalProtocol = + if (proto == HttpProtocols.`HTTP/1.0` && onInternalApi) HttpProtocols.`HTTP/1.1` + else (if (akkaHttpEntity.isChunked() && proto == HttpProtocols.`HTTP/1.0`) HttpProtocols.`HTTP/1.1` + else proto) HttpRequest( method = _method, diff --git a/otoroshi/javascript/src/apps/BackOfficeApp.js b/otoroshi/javascript/src/apps/BackOfficeApp.js index ab1d5fa997..a2746685bc 100644 --- a/otoroshi/javascript/src/apps/BackOfficeApp.js +++ b/otoroshi/javascript/src/apps/BackOfficeApp.js @@ -71,7 +71,7 @@ class BackOfficeAppContainer extends Component { lines: [], catchedError: null, env: null, - loading: true + loading: true, }; } @@ -92,10 +92,13 @@ class BackOfficeAppContainer extends Component { } componentDidMount() { - Promise.all([BackOfficeServices.env(), BackOfficeServices.fetchLines(), BackOfficeServices.findAllGroups()]) - .then(([env, lines, groups]) => { - this.setState({ env, lines, groups, loading: false }); - }) + Promise.all([ + BackOfficeServices.env(), + BackOfficeServices.fetchLines(), + BackOfficeServices.findAllGroups(), + ]).then(([env, lines, groups]) => { + this.setState({ env, lines, groups, loading: false }); + }); } componentDidCatch(e) { @@ -134,7 +137,11 @@ class BackOfficeAppContainer extends Component { {this.state.env && ( <> - + )}
diff --git a/otoroshi/javascript/src/components/Button.js b/otoroshi/javascript/src/components/Button.js index bf2e5efae0..ae92e05062 100644 --- a/otoroshi/javascript/src/components/Button.js +++ b/otoroshi/javascript/src/components/Button.js @@ -3,20 +3,22 @@ import React from 'react'; export function Button({ type = 'info', style = {}, - onClick = () => { }, + onClick = () => {}, text, children, className = '', disabled = false, ...props }) { - return -} \ No newline at end of file + return ( + + ); +} diff --git a/otoroshi/javascript/src/components/Dropdown.js b/otoroshi/javascript/src/components/Dropdown.js index 8c6073b93a..cc2776ecc9 100644 --- a/otoroshi/javascript/src/components/Dropdown.js +++ b/otoroshi/javascript/src/components/Dropdown.js @@ -1,38 +1,42 @@ import React from 'react'; -export function Dropdown({ children, className = "", style = {}, buttonStyle }) { - return
- -
    e.stopPropagation()}> -
  • - {children} -
  • -
-
-} \ No newline at end of file +export function Dropdown({ children, className = '', style = {}, buttonStyle }) { + return ( +
+ +
    e.stopPropagation()}> +
  • + {children} +
  • +
+
+ ); +} diff --git a/otoroshi/javascript/src/components/FormSelector.js b/otoroshi/javascript/src/components/FormSelector.js index 38d1fe484f..458152a67d 100644 --- a/otoroshi/javascript/src/components/FormSelector.js +++ b/otoroshi/javascript/src/components/FormSelector.js @@ -9,37 +9,39 @@ export const ENTITIES = { JWT_VERIFIERS: 'jwt-verifiers', AUTH_MODULES: 'auth-modules', API_KEYS: 'api-keys', -} +}; export function FormSelector({ onChange, entity, className = '' }) { const [showLegacyForm, toggleLegacyForm] = useState(false); useEffect(() => { loadEntities(entity); - }, [entity]) - + }, [entity]); - const loadEntities = entity => { + const loadEntities = (entity) => { try { const values = JSON.parse(localStorage.getItem(FORM_SELECTOR_KEY) || '{}'); toggleLegacyForm(values[entity]); onChange(values[entity]); } catch (e) { - console.log(e) + console.log(e); } - } + }; const saveEntities = (entity, value) => { try { const values = JSON.parse(localStorage.getItem(FORM_SELECTOR_KEY) || '{}'); - localStorage.setItem(FORM_SELECTOR_KEY, JSON.stringify({ - ...values, - [entity]: value - })); + localStorage.setItem( + FORM_SELECTOR_KEY, + JSON.stringify({ + ...values, + [entity]: value, + }) + ); } catch (e) { console.log(e); } - } + }; return ( - })} -
- - - + + ); + })} - })} - - })} - + ); + })} + - {!loadedEntities && - + className = '', +}) { + return ( +
+
+
+ + +
-
) -} \ No newline at end of file + ); +} diff --git a/otoroshi/javascript/src/components/SquareButton.js b/otoroshi/javascript/src/components/SquareButton.js index 927fbed7b0..2887728408 100644 --- a/otoroshi/javascript/src/components/SquareButton.js +++ b/otoroshi/javascript/src/components/SquareButton.js @@ -1,19 +1,19 @@ import React from 'react'; import { Button } from './Button'; -export function SquareButton({ text, icon, level = "info", ...props }) { - return -} \ No newline at end of file +export function SquareButton({ text, icon, level = 'info', ...props }) { + return ( + + ); +} diff --git a/otoroshi/javascript/src/components/TopBar.js b/otoroshi/javascript/src/components/TopBar.js index 171374283c..09115b91b7 100644 --- a/otoroshi/javascript/src/components/TopBar.js +++ b/otoroshi/javascript/src/components/TopBar.js @@ -475,7 +475,6 @@ export class TopBar extends Component { const title = `Otoroshi - ${this.props.env.instanceName}`; window.document.title = title; } - } componentWillUnmount() { @@ -525,8 +524,9 @@ export class TopBar extends Component { render() { const selected = (this.props.params || {}).lineId; return ( -