Skip to content

Commit

Permalink
fix #1922, fix #1923, fix #1924
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieuancelin committed Jun 7, 2024
1 parent ab56034 commit 57f87f4
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 23 deletions.
7 changes: 5 additions & 2 deletions otoroshi/app/auth/basic.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package otoroshi.auth

import java.security.SecureRandom
import java.util.Optional
import java.util.{Base64, Optional}
import akka.http.scaladsl.model.Uri
import akka.http.scaladsl.util.FastFuture
import com.fasterxml.jackson.annotation.JsonInclude.Include
Expand All @@ -10,7 +10,7 @@ import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
import com.google.common.base.Charsets
import com.yubico.webauthn._
import com.yubico.webauthn.data._
import otoroshi.controllers.{routes, LocalCredentialRepository}
import otoroshi.controllers.{LocalCredentialRepository, routes}
import otoroshi.env.Env
import otoroshi.models._
import org.joda.time.DateTime
Expand All @@ -23,6 +23,7 @@ import play.api.mvc._
import otoroshi.security.{IdGenerator, OtoroshiClaim}
import otoroshi.utils.{JsonPathValidator, JsonValidator}

import java.nio.charset.StandardCharsets
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}

Expand Down Expand Up @@ -306,6 +307,8 @@ case class BasicAuthModule(authConfig: BasicAuthModuleConfig) extends AuthModule
): Future[Result] = {
implicit val req = request
val redirect = request.getQueryString("redirect")
.filter(redirect => request.getQueryString("hash").contains(env.sign(s"desc=${descriptor.id}&redirect=${redirect}")))
.map(redirectBase64Encoded => new String(Base64.getUrlDecoder.decode(redirectBase64Encoded), StandardCharsets.UTF_8))
val hash = env.sign(s"${authConfig.id}:::${descriptor.id}")
env.datastores.authConfigsDataStore.generateLoginToken().flatMap { token =>
if (authConfig.basicAuth) {
Expand Down
4 changes: 4 additions & 0 deletions otoroshi/app/auth/ldap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import otoroshi.security.{IdGenerator, OtoroshiClaim}
import otoroshi.utils.{JsonPathValidator, JsonValidator, RegexPool}
import otoroshi.utils.syntax.implicits._

import java.nio.charset.StandardCharsets
import java.util.Base64
import javax.naming.ldap.{Control, InitialLdapContext}
import scala.annotation.tailrec
import scala.concurrent.{ExecutionContext, Future}
Expand Down Expand Up @@ -768,6 +770,8 @@ case class LdapAuthModule(authConfig: LdapAuthModuleConfig) extends AuthModule {
): Future[Result] = {
implicit val req = request
val redirect = request.getQueryString("redirect")
.filter(redirect => request.getQueryString("hash").contains(env.sign(s"desc=${descriptor.id}&redirect=${redirect}")))
.map(redirectBase64Encoded => new String(Base64.getUrlDecoder.decode(redirectBase64Encoded), StandardCharsets.UTF_8))
val hash = env.sign(s"${authConfig.id}:::${descriptor.id}")
env.datastores.authConfigsDataStore.generateLoginToken().flatMap { token =>
if (authConfig.basicAuth) {
Expand Down
2 changes: 2 additions & 0 deletions otoroshi/app/auth/oauth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ case class GenericOauth2Module(authConfig: OAuth2ModuleConfig) extends AuthModul
implicit val req = request

val redirect = request.getQueryString("redirect")
.filter(redirect => request.getQueryString("hash").contains(env.sign(s"desc=${descriptor.id}&redirect=${redirect}")))
.map(redirectBase64Encoded => new String(Base64.getUrlDecoder.decode(redirectBase64Encoded), StandardCharsets.UTF_8))
val clientId = authConfig.clientId
val responseType = "code"
val scope = authConfig.scope // "openid profile email name"
Expand Down
3 changes: 3 additions & 0 deletions otoroshi/app/auth/oauth1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import play.api.mvc.Results.{Ok, Redirect}
import play.api.mvc.{AnyContent, Request, RequestHeader, Result}

import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.util.Base64
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
Expand Down Expand Up @@ -331,6 +332,8 @@ case class Oauth1AuthModule(authConfig: Oauth1ModuleConfig) extends AuthModule {

if (parameters("oauth_callback_confirmed") == "true") {
val redirect = request.getQueryString("redirect")
.filter(redirect => request.getQueryString("hash").contains(env.sign(s"desc=${descriptor.id}&redirect=${redirect}")))
.map(redirectBase64Encoded => new String(Base64.getUrlDecoder.decode(redirectBase64Encoded), StandardCharsets.UTF_8))
val hash = env.sign(s"${authConfig.id}:::${descriptor.id}")
val oauth_token = parameters("oauth_token")
Redirect(s"${authConfig.authorizeURL}?oauth_token=$oauth_token&perms=read")
Expand Down
2 changes: 2 additions & 0 deletions otoroshi/app/auth/saml/SAMLClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ case class SAMLModule(authConfig: SamlAuthModuleConfig) extends AuthModule {
implicit val req: RequestHeader = request

val redirect = request.getQueryString("redirect")
.filter(redirect => request.getQueryString("hash").contains(env.sign(s"desc=${descriptor.id}&redirect=${redirect}")))
.map(redirectBase64Encoded => new String(Base64.getUrlDecoder.decode(redirectBase64Encoded), StandardCharsets.UTF_8))
val redirectTo = redirect.getOrElse(
routes.PrivateAppsController.home.absoluteURL(env.exposedRootSchemeIsHttps)
)
Expand Down
28 changes: 20 additions & 8 deletions otoroshi/app/controllers/Auth0Controller.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import play.api.libs.json._
import play.api.mvc._

import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.util.Base64
import java.util.concurrent.TimeUnit
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
Expand Down Expand Up @@ -245,7 +247,10 @@ class AuthController(
)
)
}
f(auths, route, ctx.request.getQueryString("redirect"))
val redirect = ctx.request.getQueryString("redirect")
.filter(redirect => ctx.request.getQueryString("hash").contains(env.sign(s"desc=${routeId}&redirect=${redirect}")))
.map(redirectBase64Encoded => new String(Base64.getUrlDecoder.decode(redirectBase64Encoded), StandardCharsets.UTF_8))
f(auths, route, redirect)
case JsError(errors) =>
logger.error(s"Failed to parse multi auth configuration, $errors")
NotFound(otoroshi.views.html.oto.error("Private apps are not configured", env)).vfuture
Expand Down Expand Up @@ -287,6 +292,8 @@ class AuthController(
val secStr = if (auth.clientSideSessionEnabled) s"&sec=${sec}" else ""
req
.getQueryString("redirect")
.filter(redirect => req.getQueryString("hash").contains(env.sign(s"desc=${route.id}&redirect=${redirect}")))
.map(redirectBase64Encoded => new String(Base64.getUrlDecoder.decode(redirectBase64Encoded), StandardCharsets.UTF_8))
.getOrElse(s"${req.theProtocol}://${req.theHost}${req.relativeUri}") match {
case "urn:ietf:wg:oauth:2.0:oob" => {
val redirection =
Expand Down Expand Up @@ -317,13 +324,14 @@ class AuthController(
}
case redirectTo => {
// TODO - check if ref is needed
val encodedRedirectTo = Base64.getUrlEncoder.encodeToString(redirectTo.getBytes(StandardCharsets.UTF_8))
val url = new java.net.URL(s"${req.theProtocol}://${req.theHost}${req.relativeUri}")
val host = url.getHost
val scheme = url.getProtocol
val setCookiesRedirect = url.getPort match {
case -1 =>
val redirection =
s"$scheme://$host/.well-known/otoroshi/login?route=true&sessionId=${user.randomId}&redirectTo=$redirectTo&host=$host&cp=${auth
s"$scheme://$host/.well-known/otoroshi/login?route=true&sessionId=${user.randomId}&redirectTo=$encodedRedirectTo&host=$host&cp=${auth
.routeCookieSuffix(route)}&ma=${auth.sessionMaxAge}&httpOnly=${auth.sessionCookieValues.httpOnly}&secure=${auth.sessionCookieValues.secure}${secStr}"
val hash = env.sign(redirection)
if (otoroshi.controllers.AuthController.logger.isDebugEnabled) {
Expand All @@ -334,7 +342,7 @@ class AuthController(
s"$redirection&hash=$hash"
case port =>
val redirection =
s"$scheme://$host:$port/.well-known/otoroshi/login?route=true&sessionId=${user.randomId}&redirectTo=$redirectTo&host=$host&cp=${auth
s"$scheme://$host:$port/.well-known/otoroshi/login?route=true&sessionId=${user.randomId}&redirectTo=$encodedRedirectTo&host=$host&cp=${auth
.routeCookieSuffix(route)}&ma=${auth.sessionMaxAge}&httpOnly=${auth.sessionCookieValues.httpOnly}&secure=${auth.sessionCookieValues.secure}${secStr}"
val hash = env.sign(redirection)
if (otoroshi.controllers.AuthController.logger.isDebugEnabled) {
Expand Down Expand Up @@ -394,6 +402,8 @@ class AuthController(
val secStr = if (auth.clientSideSessionEnabled) s"&sec=${sec}" else ""
req
.getQueryString("redirect")
.filter(redirect => req.getQueryString("hash").contains(env.sign(s"desc=${serviceId}&redirect=${redirect}")))
.map(redirectBase64Encoded => new String(Base64.getUrlDecoder.decode(redirectBase64Encoded), StandardCharsets.UTF_8))
.getOrElse(s"${req.theProtocol}://${req.theHost}${req.relativeUri}") match {
case "urn:ietf:wg:oauth:2.0:oob" => {
val redirection =
Expand Down Expand Up @@ -423,13 +433,14 @@ class AuthController(
)
}
case redirectTo => {
val encodedRedirectTo = Base64.getUrlEncoder.encodeToString(redirectTo.getBytes(StandardCharsets.UTF_8))
val url = new java.net.URL(s"${req.theProtocol}://${req.theHost}${req.relativeUri}")
val host = url.getHost
val scheme = url.getProtocol
val setCookiesRedirect = url.getPort match {
case -1 =>
val redirection =
s"$scheme://$host/.well-known/otoroshi/login?sessionId=${user.randomId}&redirectTo=$redirectTo&host=$host&cp=${auth
s"$scheme://$host/.well-known/otoroshi/login?sessionId=${user.randomId}&redirectTo=$encodedRedirectTo&host=$host&cp=${auth
.cookieSuffix(descriptor)}&ma=${auth.sessionMaxAge}&httpOnly=${auth.sessionCookieValues.httpOnly}&secure=${auth.sessionCookieValues.secure}${secStr}"
val hash = env.sign(redirection)
if (otoroshi.controllers.AuthController.logger.isDebugEnabled) {
Expand All @@ -440,7 +451,7 @@ class AuthController(
s"$redirection&hash=$hash"
case port =>
val redirection =
s"$scheme://$host:$port/.well-known/otoroshi/login?sessionId=${user.randomId}&redirectTo=$redirectTo&host=$host&cp=${auth
s"$scheme://$host:$port/.well-known/otoroshi/login?sessionId=${user.randomId}&redirectTo=$encodedRedirectTo&host=$host&cp=${auth
.cookieSuffix(descriptor)}&ma=${auth.sessionMaxAge}&httpOnly=${auth.sessionCookieValues.httpOnly}&secure=${auth.sessionCookieValues.secure}${secStr}"
val hash = env.sign(redirection)
if (otoroshi.controllers.AuthController.logger.isDebugEnabled) {
Expand Down Expand Up @@ -578,14 +589,15 @@ class AuthController(
env.createPrivateSessionCookies(req.theHost, user.randomId, descriptor, auth, user.some): _*
)
case redirectTo =>
val encodedRedirectTo = Base64.getUrlEncoder.encodeToString(redirectTo.getBytes(StandardCharsets.UTF_8))
val url = new java.net.URL(redirectTo)
val host = url.getHost
val scheme = url.getProtocol
val setCookiesRedirect = url.getPort match {
case -1 =>
val redirection =
s"$scheme://$host/.well-known/otoroshi/login?sessionId=${paUser.randomId}&redirectTo=$redirectTo&host=$host&cp=${auth
.cookieSuffix(descriptor)}&ma=${auth.sessionMaxAge}&httpOnly=${auth.sessionCookieValues.httpOnly}&secure=${auth.sessionCookieValues.secure}${secStr}"
s"$scheme://$host/.well-known/otoroshi/login?sessionId=${paUser.randomId}&redirectTo=$encodedRedirectTo&host=$host&cp=${auth
.cookieSuffix(descriptor)}&ma=${auth.sessionMaxAge}&httpOnly=${auth.sessionCookieValues.httpOnly}&secure=${auth.sessionCookieValues.secure}${}"
val hash = env.sign(redirection)
if (otoroshi.controllers.AuthController.logger.isDebugEnabled) {
otoroshi.controllers.AuthController.logger
Expand All @@ -594,7 +606,7 @@ class AuthController(
s"$redirection&hash=$hash"
case port =>
val redirection =
s"$scheme://$host:$port/.well-known/otoroshi/login?sessionId=${paUser.randomId}&redirectTo=$redirectTo&host=$host&cp=${auth
s"$scheme://$host:$port/.well-known/otoroshi/login?sessionId=${paUser.randomId}&redirectTo=$encodedRedirectTo&host=$host&cp=${auth
.cookieSuffix(descriptor)}&ma=${auth.sessionMaxAge}&httpOnly=${auth.sessionCookieValues.httpOnly}&secure=${auth.sessionCookieValues.secure}${secStr}"
val hash = env.sign(redirection)
if (otoroshi.controllers.AuthController.logger.isDebugEnabled) {
Expand Down
4 changes: 3 additions & 1 deletion otoroshi/app/gateway/handlers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import otoroshi.utils.http.RequestImplicits._
import otoroshi.utils.syntax.implicits._

import java.io.File
import java.nio.charset.StandardCharsets
import java.util.Base64
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
import scala.util.control.NoStackTrace
Expand Down Expand Up @@ -740,7 +742,7 @@ class GatewayRequestHandler(

def setPrivateAppsCookies() =
actionBuilder.async { req =>
val redirectToOpt: Option[String] = req.queryString.get("redirectTo").map(_.last)
val redirectToOpt: Option[String] = req.queryString.get("redirectTo").map(_.last).map(v => new String(Base64.getUrlDecoder.decode(v), StandardCharsets.UTF_8))
val sessionIdOpt: Option[String] = req.queryString.get("sessionId").map(_.last)
val hostOpt: Option[String] = req.queryString.get("host").map(_.last)
val cookiePrefOpt: Option[String] = req.queryString.get("cp").map(_.last)
Expand Down
14 changes: 10 additions & 4 deletions otoroshi/app/models/privateappsuser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import otoroshi.next.plugins.{MultiAuthModule, NgMultiAuthModuleConfig}
import otoroshi.utils.TypedMap
import otoroshi.utils.syntax.implicits.BetterSyntax

import java.nio.charset.StandardCharsets
import java.util.Base64
import scala.concurrent.duration._
import scala.concurrent.duration.Duration
import scala.concurrent.{ExecutionContext, Future}
Expand Down Expand Up @@ -297,13 +299,17 @@ object PrivateAppsUserHelper {
case Some(paUsr) =>
callDownstream(config, None, Some(paUsr))
case None => {
val redirect = req
.getQueryString("redirect")
.getOrElse(s"${req.theProtocol}://${req.theHost}${req.relativeUri}")
// val redirect = req
// .getQueryString("redirect")
// .getOrElse(s"${req.theProtocol}://${req.theHost}${req.relativeUri}")
val redirect = s"${req.theProtocol}://${req.theHost}${req.relativeUri}"
val encodedRedirect = Base64.getUrlEncoder.encodeToString(redirect.getBytes(StandardCharsets.UTF_8))
val descriptorId = descriptor.id
val hash = env.sign(s"desc=${descriptorId}&redirect=${encodedRedirect}")
val redirectTo =
env.rootScheme + env.privateAppsHost + env.privateAppsPort + otoroshi.controllers.routes.AuthController
.confidentialAppLoginPage()
.url + s"?desc=${descriptor.id}&redirect=${redirect}"
.url + s"?desc=${descriptorId}&redirect=${encodedRedirect}&hash=${hash}"
if (logger.isTraceEnabled) logger.trace("should redirect to " + redirectTo)
descriptor.authConfigRef match {
case None =>
Expand Down
Loading

0 comments on commit 57f87f4

Please sign in to comment.