Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .github/workflows/build_container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ jobs:
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
echo hikari.maximumPoolSize=20 >> obp-api/src/main/resources/props/test.default.props
echo write_metrics=false >> obp-api/src/main/resources/props/test.default.props
# Permissions granted to runtime-compiled dynamic-endpoint code inside the security sandbox
# (mirrors default.props / production.default.props). Required so dynamic resource-doc bodies
# can do JSON extraction (reflection) and read OBP props (getenv); without it the sandbox
# denies these and DynamicResourceDocTest's native-execution scenarios fail.
echo 'dynamic_code_sandbox_permissions=[new java.net.NetPermission("specifyStreamHandler"), new java.lang.reflect.ReflectPermission("suppressAccessChecks"), new java.lang.RuntimePermission("getenv.*"), new java.util.PropertyPermission("cglib.useCache", "read"), new java.util.PropertyPermission("net.sf.cglib.test.stressHashCodes", "read"), new java.util.PropertyPermission("cglib.debugLocation", "read"), new java.lang.RuntimePermission("accessDeclaredMembers"), new java.lang.RuntimePermission("getClassLoader")]' >> obp-api/src/main/resources/props/test.default.props

- name: Run tests — shard ${{ matrix.shard }} (${{ matrix.name }})
run: |
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/build_pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ jobs:
# there's no mail server in CI. That surfaces as 500 in any test that
# hits an endpoint triggering the notification (v5 consent flows, etc.).
echo mail.test.mode=true >> obp-api/src/main/resources/props/test.default.props
# Permissions granted to runtime-compiled dynamic-endpoint code inside the security sandbox
# (mirrors default.props / production.default.props). Required so dynamic resource-doc bodies
# can do JSON extraction (reflection) and read OBP props (getenv); without it the sandbox
# denies these and DynamicResourceDocTest's native-execution scenarios fail.
echo 'dynamic_code_sandbox_permissions=[new java.net.NetPermission("specifyStreamHandler"), new java.lang.reflect.ReflectPermission("suppressAccessChecks"), new java.lang.RuntimePermission("getenv.*"), new java.util.PropertyPermission("cglib.useCache", "read"), new java.util.PropertyPermission("net.sf.cglib.test.stressHashCodes", "read"), new java.util.PropertyPermission("cglib.debugLocation", "read"), new java.lang.RuntimePermission("accessDeclaredMembers"), new java.lang.RuntimePermission("getClassLoader")]' >> obp-api/src/main/resources/props/test.default.props

- name: Run tests — shard ${{ matrix.shard }} (${{ matrix.name }})
run: |
Expand Down
15 changes: 15 additions & 0 deletions obp-api/src/main/resources/props/test.default.props.template
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,18 @@ allow_public_views =true
# requests + N background queries = 2*N connections needed. Default of 10 is exhausted by
# the 10-thread concurrency tests. Set to 20 to provide headroom.
hikari.maximumPoolSize=20

# Permissions granted to runtime-compiled dynamic-endpoint code inside the security sandbox.
# Mirrors default.props / production.default.props. Required so dynamic resource-doc bodies can do
# JSON extraction (reflection) and read OBP props (getenv); without it the sandbox denies these and
# dynamic-endpoint EXECUTION cannot run (only metadata CRUD / compilation). See DynamicResourceDocTest.
dynamic_code_sandbox_permissions=[\
new java.net.NetPermission("specifyStreamHandler"),\
new java.lang.reflect.ReflectPermission("suppressAccessChecks"),\
new java.lang.RuntimePermission("getenv.*"),\
new java.util.PropertyPermission("cglib.useCache", "read"),\
new java.util.PropertyPermission("net.sf.cglib.test.stressHashCodes", "read"),\
new java.util.PropertyPermission("cglib.debugLocation", "read"),\
new java.lang.RuntimePermission("accessDeclaredMembers"),\
new java.lang.RuntimePermission("getClassLoader")\
]
63 changes: 36 additions & 27 deletions obp-api/src/main/scala/code/api/OBPRestHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -574,37 +574,46 @@ trait OBPRestHelper extends RestHelper with MdcLoggable {
*/

def oauthServe(handler: PartialFunction[Req, CallContext => Box[JsonResponse]], rd: Option[ResourceDoc] = None): Unit = {
val obpHandler : PartialFunction[Req, () => Box[LiftResponse]] = {
new PartialFunction[Req, () => Box[LiftResponse]] {
def apply(r : Req): () => Box[LiftResponse] = {
//check (in that order):
//if request is correct json
//if request matches PartialFunction cases for each defined url
//if request has correct oauth headers
val startTime = Helpers.now
val response = failIfBadAuthorizationHeader(rd) {
failIfBadJSON(r, handler)
}
val endTime = Helpers.now
WriteMetricUtil.writeEndpointMetric(startTime, endTime.getTime - startTime.getTime, rd)
response
serve(buildOAuthHandler(handler, rd))
}

/**
* Build the oauth-wrapped Lift handler that `oauthServe` would otherwise register directly into
* Lift's statelessDispatch. Extracted as a public method so the in-process Lift adapter in
* code.api.dynamic.endpoint.Http4sDynamicEndpoint can construct the exact same wrapped form
* (failIfBadAuthorizationHeader { failIfBadJSON } + endpoint metric) for the dynamic-endpoint
* routes and apply it directly — without registering into statelessDispatch. Behaviour for the
* normal oauthServe path is unchanged (oauthServe now just `serve(buildOAuthHandler(...))`).
*/
def buildOAuthHandler(handler: PartialFunction[Req, CallContext => Box[JsonResponse]], rd: Option[ResourceDoc] = None): PartialFunction[Req, () => Box[LiftResponse]] = {
new PartialFunction[Req, () => Box[LiftResponse]] {
def apply(r : Req): () => Box[LiftResponse] = {
//check (in that order):
//if request is correct json
//if request matches PartialFunction cases for each defined url
//if request has correct oauth headers
val startTime = Helpers.now
val response = failIfBadAuthorizationHeader(rd) {
failIfBadJSON(r, handler)
}
def isDefinedAt(r : Req) = {
//if the content-type is json and json parsing failed, simply accept call but then fail in apply() before
//the url cases don't match because json failed
r.json_? match {
case true =>
//Try to evaluate the json
r.json match {
case Failure(msg, _, _) => true
case _ => handler.isDefinedAt(r)
}
case false => handler.isDefinedAt(r)
}
val endTime = Helpers.now
WriteMetricUtil.writeEndpointMetric(startTime, endTime.getTime - startTime.getTime, rd)
response
}
def isDefinedAt(r : Req) = {
//if the content-type is json and json parsing failed, simply accept call but then fail in apply() before
//the url cases don't match because json failed
r.json_? match {
case true =>
//Try to evaluate the json
r.json match {
case Failure(msg, _, _) => true
case _ => handler.isDefinedAt(r)
}
case false => handler.isDefinedAt(r)
}
}
}
serve(obpHandler)
}

override protected def serve(handler: PartialFunction[Req, () => Box[LiftResponse]]) : Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth

val resourceDocs = requestedApiVersion match {
case ApiVersion.v7_0_0 => code.api.v7_0_0.Http4s700.allResourceDocs // Use aggregated docs for v7.0.0
case ConstantsBG.`berlinGroupVersion1` => code.api.berlin.group.v1_3.Http4sBGv13.resourceDocs
case ConstantsBG.`berlinGroupVersion2` => code.api.berlin.group.v2.Http4sBGv2.resourceDocs
case ApiVersion.v6_0_0 => OBPAPI6_0_0.allResourceDocs
case ApiVersion.v5_1_0 => OBPAPI5_1_0.allResourceDocs
Expand All @@ -146,6 +147,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth

val versionRoutes = requestedApiVersion match {
case ApiVersion.v7_0_0 => Nil
case ConstantsBG.`berlinGroupVersion1` => Nil
case ConstantsBG.`berlinGroupVersion2` => Nil
case ApiVersion.v6_0_0 => OBPAPI6_0_0.routes
case ApiVersion.v5_1_0 => OBPAPI5_1_0.routes
Expand Down Expand Up @@ -175,6 +177,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
// Only return the resource docs that have available routes
val activeResourceDocs = requestedApiVersion match {
case ApiVersion.v7_0_0 => resourceDocs
case ConstantsBG.`berlinGroupVersion1` => resourceDocs // fully on http4s — no Lift route filter
case ConstantsBG.`berlinGroupVersion2` => resourceDocs
case ApiVersion.v1_2_1 => resourceDocs
case ApiVersion.v6_0_0 => resourceDocs // fully on http4s — no Lift route filter
Expand All @@ -189,6 +192,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
case ApiVersion.v1_4_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v1_3_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.`dynamic-entity` => resourceDocs // runtime CRUD now on Http4sDynamicEntity; routes are Nil, skip Lift-route filter
case ApiVersion.`dynamic-endpoint` => resourceDocs // dispatch now on Http4sDynamicEndpoint (proxy + native Piece C); routes carry only the stub, skip Lift-route filter
case _ => resourceDocs.filter(rd => versionRoutesClasses.contains(rd.partialFunction.getClass))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package code.api.berlin.group.v1_3

import cats.data.{Kleisli, OptionT}
import cats.effect._
import code.api.berlin.group.ConstantsBG
import code.api.util.APIUtil.ResourceDoc
import code.api.util.http4s.ResourceDocMiddleware
import code.util.Helper.MdcLoggable
import org.http4s._

import scala.collection.mutable.ArrayBuffer

/**
* Native http4s aggregator for Berlin Group v1.3, replacing the Lift
* `OBP_BERLIN_GROUP_1_3` statelessDispatch registration. Mirrors `Http4sBGv2`.
*
* Groups: AIS / PIS / SigningBaskets / PIIS. Added incrementally.
*/
object Http4sBGv13 extends MdcLoggable {

type HttpF[A] = OptionT[IO, A]

val implementedInApiVersion = ConstantsBG.berlinGroupVersion1

val resourceDocs: ArrayBuffer[ResourceDoc] =
Http4sBGv13AIS.resourceDocs ++
Http4sBGv13PIS.resourceDocs ++
Http4sBGv13PIIS.resourceDocs ++
Http4sBGv13SigningBaskets.resourceDocs

val allRoutes: HttpRoutes[IO] = Kleisli[HttpF, Request[IO], Response[IO]] { req =>
Http4sBGv13AIS.routes(req)
.orElse(Http4sBGv13PIS.routes(req))
.orElse(Http4sBGv13PIIS.routes(req))
.orElse(Http4sBGv13SigningBaskets.routes(req))
}

val wrappedRoutes: HttpRoutes[IO] = ResourceDocMiddleware.apply(resourceDocs)(allRoutes)
}
Loading