Skip to content

Commit bae658d

Browse files
authored
Merge pull request #2742 from simonredfern/develop
ConsentItem table so we don't have to extract JWT for bank_id endpoints
2 parents 39a0950 + 7f1af50 commit bae658d

File tree

14 files changed

+333
-145
lines changed

14 files changed

+333
-145
lines changed

flushall_build_and_run.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ tail -n 3 -f build.log &
111111
TAIL_PID=$!
112112
mvn -pl obp-api -am clean package -DskipTests=true -Dmaven.test.skip=true -T 4 > build.log 2>&1
113113
BUILD_EXIT=$?
114-
kill $TAIL_PID 2>/dev/null
115-
wait $TAIL_PID 2>/dev/null
114+
kill $TAIL_PID 2>/dev/null || true
115+
wait $TAIL_PID 2>/dev/null || true
116116

117117
if [ $BUILD_EXIT -ne 0 ]; then
118118
echo ""

obp-api/src/main/scala/bootstrap/liftweb/Boot.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ import code.branches.MappedBranch
6363
import code.cardattribute.MappedCardAttribute
6464
import code.cards.{MappedPhysicalCard, PinReset}
6565
import code.connectormethod.ConnectorMethod
66-
import code.consent.{ConsentRequest, MappedConsent}
66+
import code.consent.{ConsentItem, ConsentRequest, MappedConsent}
6767
import code.consumer.Consumers
6868
import code.model.Consumer
6969
import code.context.{MappedConsentAuthContext, MappedUserAuthContext, MappedUserAuthContextUpdate}
@@ -1120,6 +1120,7 @@ object ToSchemify {
11201120
MappedCustomerIdMapping,
11211121
MappedProductAttribute,
11221122
MappedConsent,
1123+
ConsentItem,
11231124
ConsentRequest,
11241125
MigrationScriptLog,
11251126
MethodRouting,

obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4601,9 +4601,10 @@ object SwaggerDefinitionsJSON {
46014601
last_action_date = dateExample.value,
46024602
last_usage_date = dateTimeExample.value,
46034603
jwt = jwtExample.value,
4604-
jwt_payload = Some(consentJWT),
4604+
jwt_payload = """{"createdByUserId":"user-id","sub":"subject","iss":"issuer","aud":"audience","jti":"jwt-id","iat":1611749820,"nbf":1611749820,"exp":1611753420,"request_headers":[],"name":null,"email":null,"entitlements":[],"views":[],"access":null}""",
46054605
api_standard = "Berlin Group",
46064606
api_version = "v1.3",
4607+
jwt_expires_at = dateTimeExample.value,
46074608
)
46084609

46094610
lazy val consentsInfoJsonV510 = ConsentsInfoJsonV510(

obp-api/src/main/scala/code/api/util/APIUtil.scala

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,19 +1271,13 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
12711271
roleName <- getHttpParamValuesByName(httpParams, "role_name")
12721272
httpStatusCode <- getHttpParamValuesByName(httpParams, "http_status_code")
12731273
}yield{
1274-
/**
1275-
* sortBy is currently disabled as it would open up a security hole:
1276-
*
1277-
* sortBy as currently implemented will take in a parameter that searches on the mongo field names. The issue here
1278-
* is that it will sort on the true value, and not the moderated output. So if a view is supposed to return an alias name
1279-
* rather than the true value, but someone uses sortBy on the other bank account name/holder, not only will the returned data
1280-
* have the wrong order, but information about the true account holder name will be exposed due to its position in the sorted order
1281-
*
1282-
* This applies to all fields that can have their data concealed... which in theory will eventually be most/all
1283-
*
1284-
*/
1285-
//val sortBy = json.header("obp_sort_by")
1286-
val ordering = OBPOrdering(None, sortDirection)
1274+
// Extract the sort field name from the sort_by query param (e.g. "url", "date").
1275+
// OBPOrdering expects Option[String], but sortBy is an OBPQueryParam.
1276+
val sortField = sortBy match {
1277+
case OBPSortBy(v) => Some(v)
1278+
case _ => None
1279+
}
1280+
val ordering = OBPOrdering(sortField, sortDirection)
12871281
//This guarantee the order
12881282
List(limit, offset, ordering, sortBy, fromDate, toDate,
12891283
anon, status, consumerId, azp, iss, consentId, userId, providerProviderId, url, appName, implementedByPartialFunction, implementedInVersion,

obp-api/src/main/scala/code/api/util/Glossary.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5111,6 +5111,20 @@ object Glossary extends MdcLoggable {
51115111
|
51125112
""")
51135113

5114+
glossaryItems += GlossaryItem(
5115+
title = "SDKs",
5116+
description =
5117+
s"""
5118+
|# SDKs
5119+
|
5120+
|OBP SDKs (Software Development Kits) are client libraries that make it easier to interact with the OBP API from various programming languages.
5121+
|
5122+
|SDKs are available for multiple languages including Python, Java, Scala, PHP, C#, Javascript and more.
5123+
|
5124+
|For more information see [OBP SDKs on GitHub](https://github.com/OpenBankProject/OBP-SDKs).
5125+
|
5126+
""")
5127+
51145128
///////////////////////////////////////////////////////////////////
51155129
// NOTE! Some glossary items are generated in ExampleValue.scala
51165130
//////////////////////////////////////////////////////////////////

obp-api/src/main/scala/code/api/util/migration/Migration.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ object Migration extends MdcLoggable {
113113
addMetricView(startedBeforeSchemifier)
114114
addConsentView(startedBeforeSchemifier)
115115
updateConsentViewAddJwtPayload(startedBeforeSchemifier)
116+
updateConsentViewAddJwtExpiresAt(startedBeforeSchemifier)
116117
updateAccountAccessWithViewsViewUnionAll(startedBeforeSchemifier)
117118
}
118119

@@ -613,6 +614,18 @@ object Migration extends MdcLoggable {
613614
}
614615
}
615616

617+
private def updateConsentViewAddJwtExpiresAt(startedBeforeSchemifier: Boolean): Boolean = {
618+
if(startedBeforeSchemifier == true) {
619+
logger.warn(s"Migration.database.updateConsentViewAddJwtExpiresAt(true) cannot be run before Schemifier.")
620+
true
621+
} else {
622+
val name = nameOf(updateConsentViewAddJwtExpiresAt(startedBeforeSchemifier))
623+
runOnce(name) {
624+
MigrationOfConsentView.addConsentView(name)
625+
}
626+
}
627+
}
628+
616629
private def addAccountAccessWithViewsView(startedBeforeSchemifier: Boolean): Boolean = {
617630
if(startedBeforeSchemifier == true) {
618631
logger.warn(s"Migration.database.addAccountAccessWithViewsView(true) cannot be run before Schemifier.")

obp-api/src/main/scala/code/api/util/migration/MigrationOfConsentView.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ object MigrationOfConsentView {
3737
| mnote AS note,
3838
| mfrequencyperday AS frequency_per_day,
3939
| musessofartodaycounter AS uses_so_far_today_counter,
40-
| mjsonwebtokenpayload AS jwt_payload
40+
| mjsonwebtokenpayload AS jwt_payload,
41+
| jwt_expires_at AS jwt_expires_at
4142
|FROM mappedconsent;
4243
|""".stripMargin
4344
case _ =>
@@ -60,7 +61,8 @@ object MigrationOfConsentView {
6061
| mnote AS note,
6162
| mfrequencyperday AS frequency_per_day,
6263
| musessofartodaycounter AS uses_so_far_today_counter,
63-
| mjsonwebtokenpayload AS jwt_payload
64+
| mjsonwebtokenpayload AS jwt_payload,
65+
| jwt_expires_at AS jwt_expires_at
6466
|FROM mappedconsent;
6567
|""".stripMargin
6668
}

obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import code.api.v3_0_0.{CreateViewJsonV300, JSONFactory300}
2525
import code.api.v3_1_0.JSONFactory310._
2626
import code.bankconnectors.rest.RestConnector_vMar2019
2727
import code.bankconnectors.{Connector, LocalMappedConnector}
28-
import code.consent.{ConsentStatus, Consents, MappedConsent}
28+
import code.consent.{ConsentStatus, Consents, DoobieConsentQueries, MappedConsent}
2929
import code.consumer.Consumers
3030
import code.entitlement.Entitlement
3131
import code.loginattempts.LoginAttempt
@@ -3726,6 +3726,10 @@ trait APIMethods310 {
37263726
|
37273727
|${userAuthenticationMessage(true)}
37283728
|
3729+
|1 limit (for pagination: defaults to 50) eg:limit=200
3730+
|
3731+
|2 offset (for pagination: zero index, defaults to 0) eg: offset=10
3732+
|
37293733
""".stripMargin,
37303734
EmptyBody,
37313735
consentsJsonV310,
@@ -3739,12 +3743,32 @@ trait APIMethods310 {
37393743
lazy val getConsents: OBPEndpoint = {
37403744
case "banks" :: BankId(bankId) :: "my" :: "consents" :: Nil JsonGet _ => {
37413745
cc => implicit val ec = EndpointContext(Some(cc))
3746+
val url = cc.url
3747+
val limitParam = APIUtil.getHttpRequestUrlParam(url, "limit") match {
3748+
case s if s.nonEmpty => scala.util.Try(s.toInt).getOrElse(50)
3749+
case _ => 50
3750+
}
3751+
val offsetParam = APIUtil.getHttpRequestUrlParam(url, "offset") match {
3752+
case s if s.nonEmpty => scala.util.Try(s.toInt).getOrElse(0)
3753+
case _ => 0
3754+
}
37423755
for {
37433756
(Full(user), callContext) <- authenticatedAccess(cc)
37443757
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
3745-
consents <- Future(Consents.consentProvider.vend.getConsentsByUser(user.userId))
3758+
rows <- Future {
3759+
DoobieConsentQueries.getConsentsByUserAndBank(
3760+
userId = user.userId,
3761+
bankId = bankId.value,
3762+
status = None,
3763+
limit = limitParam,
3764+
offset = offsetParam,
3765+
sortField = "created_date",
3766+
sortDirection = "desc"
3767+
)
3768+
}
37463769
} yield {
3747-
(JSONFactory310.createConsentsJsonV310(consents), HttpCode.`200`(callContext))
3770+
val consents = rows.map(r => ConsentJsonV310(r.consentId, r.jwt.getOrElse(""), r.status))
3771+
(ConsentsJsonV310(consents), HttpCode.`200`(callContext))
37483772
}
37493773
}
37503774
}

obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import code.authtypevalidation.JsonAuthTypeValidation
4242
import code.bankconnectors.LocalMappedConnectorInternal._
4343
import code.bankconnectors.{Connector, DynamicConnector, InternalConnector, LocalMappedConnectorInternal}
4444
import code.connectormethod.{JsonConnectorMethod, JsonConnectorMethodMethodBody}
45-
import code.consent.{ConsentStatus, Consents}
45+
import code.consent.{ConsentStatus, Consents, DoobieConsentQueries}
4646
import code.dynamicEntity.DynamicEntityCommons
4747
import code.dynamicMessageDoc.JsonDynamicMessageDoc
4848
import code.dynamicResourceDoc.JsonDynamicResourceDoc
@@ -11479,6 +11479,10 @@ trait APIMethods400 extends MdcLoggable {
1147911479
|
1148011480
|${userAuthenticationMessage(true)}
1148111481
|
11482+
|1 limit (for pagination: defaults to 50) eg:limit=200
11483+
|
11484+
|2 offset (for pagination: zero index, defaults to 0) eg: offset=10
11485+
|
1148211486
""".stripMargin,
1148311487
EmptyBody,
1148411488
consentsJsonV400,
@@ -11494,19 +11498,33 @@ trait APIMethods400 extends MdcLoggable {
1149411498
case "banks" :: BankId(bankId) :: "my" :: "consents" :: Nil JsonGet _ => {
1149511499
cc =>
1149611500
implicit val ec = EndpointContext(Some(cc))
11501+
val url = cc.url
11502+
val limitParam = getHttpRequestUrlParam(url, "limit") match {
11503+
case s if s.nonEmpty => scala.util.Try(s.toInt).getOrElse(50)
11504+
case _ => 50
11505+
}
11506+
val offsetParam = getHttpRequestUrlParam(url, "offset") match {
11507+
case s if s.nonEmpty => scala.util.Try(s.toInt).getOrElse(0)
11508+
case _ => 0
11509+
}
1149711510
for {
11498-
consents <- Future {
11499-
Consents.consentProvider.vend
11500-
.getConsentsByUser(cc.userId)
11501-
.sortBy(i => (i.creationDateTime, i.apiStandard))
11502-
.reverse
11511+
rows <- Future {
11512+
DoobieConsentQueries.getConsentsByUserAndBank(
11513+
userId = cc.userId,
11514+
bankId = bankId.value,
11515+
status = None,
11516+
limit = limitParam,
11517+
offset = offsetParam,
11518+
sortField = "created_date",
11519+
sortDirection = "desc"
11520+
)
1150311521
}
1150411522
} yield {
11505-
val consentsOfBank = Consent.filterByBankId(consents, bankId)
11506-
(
11507-
JSONFactory400.createConsentsJsonV400(consentsOfBank),
11508-
HttpCode.`200`(cc)
11509-
)
11523+
val consents = rows.map(r => ConsentJsonV400(
11524+
r.consentId, r.jwt.getOrElse(""), r.status,
11525+
r.apiStandard.getOrElse(""), r.apiVersion.getOrElse("")
11526+
))
11527+
(ConsentsJsonV400(consents), HttpCode.`200`(cc))
1151011528
}
1151111529
}
1151211530
}
@@ -12109,6 +12127,10 @@ trait APIMethods400 extends MdcLoggable {
1210912127
|
1211012128
|${userAuthenticationMessage(true)}
1211112129
|
12130+
|1 limit (for pagination: defaults to 50) eg:limit=200
12131+
|
12132+
|2 offset (for pagination: zero index, defaults to 0) eg: offset=10
12133+
|
1211212134
|""".stripMargin,
1211312135
EmptyBody,
1211412136
apiCollectionsJson400,
@@ -12122,12 +12144,22 @@ trait APIMethods400 extends MdcLoggable {
1212212144
lazy val getMyApiCollections: OBPEndpoint = {
1212312145
case "my" :: "api-collections" :: Nil JsonGet _ => { cc =>
1212412146
implicit val ec = EndpointContext(Some(cc))
12147+
val url = cc.url
12148+
val limitParam = getHttpRequestUrlParam(url, "limit") match {
12149+
case s if s.nonEmpty => scala.util.Try(s.toInt).getOrElse(50)
12150+
case _ => 50
12151+
}
12152+
val offsetParam = getHttpRequestUrlParam(url, "offset") match {
12153+
case s if s.nonEmpty => scala.util.Try(s.toInt).getOrElse(0)
12154+
case _ => 0
12155+
}
1212512156
for {
1212612157
(apiCollections, callContext) <- NewStyle.function
1212712158
.getApiCollectionsByUserId(cc.userId, Some(cc))
1212812159
} yield {
12160+
val paginated = apiCollections.drop(offsetParam).take(limitParam)
1212912161
(
12130-
JSONFactory400.createApiCollectionsJsonV400(apiCollections),
12162+
JSONFactory400.createApiCollectionsJsonV400(paginated),
1213112163
HttpCode.`200`(callContext)
1213212164
)
1213312165
}

0 commit comments

Comments
 (0)