Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
case ApiVersion.v6_0_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v5_1_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v5_0_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v4_0_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v3_1_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v3_0_0 => resourceDocs // fully on http4s — no Lift route filter
case _ => resourceDocs.filter(rd => versionRoutesClasses.contains(rd.partialFunction.getClass))
}

Expand Down
28 changes: 14 additions & 14 deletions obp-api/src/main/scala/code/api/util/http4s/Http4sSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package code.api.util.http4s

import cats.effect._
import code.api.util.APIUtil.{ResourceDoc, getPropsAsBoolValue}
import code.api.util.ErrorMessages.InvalidJsonFormat
import code.api.util.ErrorMessages.{AuthenticatedUserIsRequired, InvalidJsonFormat}
import code.api.util.{AuthHeaderParser, CallContext, RemoteIpUtil, WriteMetricUtil}
import code.util.Helper.MdcLoggable
import com.openbankproject.commons.model.{Bank, BankAccount, CounterpartyTrait, User, View}
Expand Down Expand Up @@ -146,7 +146,7 @@ object Http4sRequestAttributes {
def withUser[A](req: Request[IO])(f: (User, CallContext) => Future[A])(implicit formats: Formats): IO[Response[IO]] = {
implicit val cc: CallContext = req.callContext
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
result <- RequestScopeConnection.fromFuture(f(user, cc))
} yield result
io.attempt.flatMap {
Expand Down Expand Up @@ -178,7 +178,7 @@ object Http4sRequestAttributes {
def withUserAndBank[A](req: Request[IO])(f: (User, Bank, CallContext) => Future[A])(implicit formats: Formats): IO[Response[IO]] = {
implicit val cc: CallContext = req.callContext
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
bank <- IO.fromOption(cc.bank)(new RuntimeException("Bank not found in CallContext"))
result <- RequestScopeConnection.fromFuture(f(user, bank, cc))
} yield result
Expand Down Expand Up @@ -243,7 +243,7 @@ object Http4sRequestAttributes {
case Left(msg) => ErrorResponseConverter.createErrorResponse(400, msg, cc).flatTap(recordMetric(msg, _))
case Right(body) =>
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
result <- RequestScopeConnection.fromFuture(f(user, body, cc))
} yield result
io.attempt.flatMap {
Expand All @@ -263,7 +263,7 @@ object Http4sRequestAttributes {
case Left(msg) => ErrorResponseConverter.createErrorResponse(400, msg, cc).flatTap(recordMetric(msg, _))
case Right(body) =>
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
result <- RequestScopeConnection.fromFuture(f(user, body, cc))
} yield result
io.attempt.flatMap {
Expand All @@ -285,7 +285,7 @@ object Http4sRequestAttributes {
case Left(msg) => ErrorResponseConverter.createErrorResponse(400, msg, cc).flatTap(recordMetric(msg, _))
case Right(body) =>
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
bank <- IO.fromOption(cc.bank)(new RuntimeException("Bank not found in CallContext"))
result <- RequestScopeConnection.fromFuture(f(user, bank, body, cc))
} yield result
Expand All @@ -306,7 +306,7 @@ object Http4sRequestAttributes {
case Left(msg) => ErrorResponseConverter.createErrorResponse(400, msg, cc).flatTap(recordMetric(msg, _))
case Right(body) =>
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
bank <- IO.fromOption(cc.bank)(new RuntimeException("Bank not found in CallContext"))
result <- RequestScopeConnection.fromFuture(f(user, bank, body, cc))
} yield result
Expand All @@ -326,7 +326,7 @@ object Http4sRequestAttributes {
def withBankAccount[A](req: Request[IO])(f: (User, BankAccount, CallContext) => Future[A])(implicit formats: Formats): IO[Response[IO]] = {
implicit val cc: CallContext = req.callContext
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
bankAccount <- IO.fromOption(cc.bankAccount)(new RuntimeException("BankAccount not found in CallContext"))
result <- RequestScopeConnection.fromFuture(f(user, bankAccount, cc))
} yield result
Expand All @@ -343,7 +343,7 @@ object Http4sRequestAttributes {
def withViewCreated[A](req: Request[IO])(f: (User, BankAccount, View, CallContext) => Future[A])(implicit formats: Formats): IO[Response[IO]] = {
implicit val cc: CallContext = req.callContext
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
bankAccount <- IO.fromOption(cc.bankAccount)(new RuntimeException("BankAccount not found in CallContext"))
view <- IO.fromOption(cc.view)(new RuntimeException("View not found in CallContext"))
result <- RequestScopeConnection.fromFuture(f(user, bankAccount, view, cc))
Expand All @@ -366,7 +366,7 @@ object Http4sRequestAttributes {
case Left(msg) => ErrorResponseConverter.createErrorResponse(400, msg, cc).flatTap(recordMetric(msg, _))
case Right(body) =>
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
bankAccount <- IO.fromOption(cc.bankAccount)(new RuntimeException("BankAccount not found in CallContext"))
view <- IO.fromOption(cc.view)(new RuntimeException("View not found in CallContext"))
result <- RequestScopeConnection.fromFuture(f(user, bankAccount, view, body, cc))
Expand All @@ -387,7 +387,7 @@ object Http4sRequestAttributes {
def withView[A](req: Request[IO])(f: (User, BankAccount, View, CallContext) => Future[A])(implicit formats: Formats): IO[Response[IO]] = {
implicit val cc: CallContext = req.callContext
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
bankAccount <- IO.fromOption(cc.bankAccount)(new RuntimeException("BankAccount not found in CallContext"))
view <- IO.fromOption(cc.view)(new RuntimeException("View not found in CallContext"))
result <- RequestScopeConnection.fromFuture(f(user, bankAccount, view, cc))
Expand All @@ -405,7 +405,7 @@ object Http4sRequestAttributes {
def withCounterparty[A](req: Request[IO])(f: (User, BankAccount, View, CounterpartyTrait, CallContext) => Future[A])(implicit formats: Formats): IO[Response[IO]] = {
implicit val cc: CallContext = req.callContext
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
bankAccount <- IO.fromOption(cc.bankAccount)(new RuntimeException("BankAccount not found in CallContext"))
view <- IO.fromOption(cc.view)(new RuntimeException("View not found in CallContext"))
counterparty <- IO.fromOption(cc.counterparty)(new RuntimeException("Counterparty not found in CallContext"))
Expand Down Expand Up @@ -463,7 +463,7 @@ object Http4sRequestAttributes {
def withUserDelete(req: Request[IO])(f: (User, CallContext) => Future[_]): IO[Response[IO]] = {
implicit val cc: CallContext = req.callContext
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
result <- RequestScopeConnection.fromFuture(f(user, cc))
} yield result
io.attempt.flatMap {
Expand All @@ -479,7 +479,7 @@ object Http4sRequestAttributes {
def withUserAndBankDelete(req: Request[IO])(f: (User, Bank, CallContext) => Future[_]): IO[Response[IO]] = {
implicit val cc: CallContext = req.callContext
val io = for {
user <- IO.fromOption(cc.user.toOption)(new RuntimeException("User not found in CallContext"))
user <- IO.fromOption(cc.user.toOption)(new RuntimeException(AuthenticatedUserIsRequired))
bank <- IO.fromOption(cc.bank)(new RuntimeException("Bank not found in CallContext"))
result <- RequestScopeConnection.fromFuture(f(user, bank, cc))
} yield result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,17 @@ object ResourceDocMiddleware extends MdcLoggable {
// No matching ResourceDoc: fallback to original route (NO transaction scope opened).
// Attach the basic CC so req.callContext works in the inner route even without a doc match.
// Carry the cached body forward so the bridge cascade can still read it.
routes.run(reqWithCachedBody.withAttribute(Http4sRequestAttributes.callContextKey, cc))
// Best-effort authentication: populate cc.user from request credentials so that
// withUser/withUserAndBank handlers return 401/403 correctly (e.g. empty path segments
// that bypass ResourceDocMatcher but still match a route pattern).
OptionT.liftF(
IO.fromFuture(IO(APIUtil.anonymousAccess(cc))).map {
case (Full(user), Some(updatedCC)) => reqWithCachedBody.withAttribute(Http4sRequestAttributes.callContextKey, updatedCC.copy(user = Full(user)))
case (Full(user), None) => reqWithCachedBody.withAttribute(Http4sRequestAttributes.callContextKey, cc.copy(user = Full(user)))
case (_, Some(updatedCC)) => reqWithCachedBody.withAttribute(Http4sRequestAttributes.callContextKey, updatedCC)
case _ => reqWithCachedBody.withAttribute(Http4sRequestAttributes.callContextKey, cc)
}.recover { case _ => reqWithCachedBody.withAttribute(Http4sRequestAttributes.callContextKey, cc) }
).flatMap(routes.run)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2472,5 +2472,5 @@ object Http4s121 {
}
}

val wrappedRoutesV121Services: HttpRoutes[IO] = Implementations1_2_1.allRoutesWithMiddleware
lazy val wrappedRoutesV121Services: HttpRoutes[IO] = Implementations1_2_1.allRoutesWithMiddleware
}
Loading
Loading