mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 17:17:09 +00:00
Merge remote-tracking branch 'Simon/develop' into develop
# Conflicts: # obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala
This commit is contained in:
commit
d7e30ec856
5
.github/Dockerfile_PreBuild
vendored
5
.github/Dockerfile_PreBuild
vendored
@ -1,5 +1,10 @@
|
||||
FROM jetty:9.4-jdk11-alpine
|
||||
|
||||
ENV JMX_EXPORTER_VERSION=1.2.0
|
||||
|
||||
# To enable add "-javaagent:$JETTY_BASE/jmx-exporter.jar=8090:$JETTY_BASE/prometheus_config.yml" to the JAVA_OPTIONS
|
||||
RUN wget https://github.com/prometheus/jmx_exporter/releases/download/$JMX_EXPORTER_VERSION/jmx_prometheus_javaagent-$JMX_EXPORTER_VERSION.jar -o /var/lib/jetty/jmx-exporter.jar
|
||||
COPY .github/jmx_exporter.config /var/lib/jetty/prometheus_config.yml
|
||||
# Copy OBP source code
|
||||
# Copy build artifact (.war file) into jetty from 'maven' stage.
|
||||
COPY /obp-api/target/obp-api-1.*.war /var/lib/jetty/webapps/ROOT.war
|
||||
|
||||
15
.github/jmx_exporter.config
vendored
Normal file
15
.github/jmx_exporter.config
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
lowercaseOutputLabelNames: true
|
||||
lowercaseOutputName: true
|
||||
whitelistObjectNames: ["java.lang:type=OperatingSystem"]
|
||||
blacklistObjectNames: []
|
||||
rules:
|
||||
- pattern: 'java.lang<type=OperatingSystem><>(committed_virtual_memory|free_physical_memory|free_swap_space|total_physical_memory|total_swap_space)_size:'
|
||||
name: os_$1_bytes
|
||||
type: GAUGE
|
||||
attrNameSnakeCase: true
|
||||
- pattern: 'java.lang<type=OperatingSystem><>((?!process_cpu_time)\w+):'
|
||||
name: os_$1
|
||||
type: GAUGE
|
||||
attrNameSnakeCase: true
|
||||
- pattern: ".*"
|
||||
35
.github/workflows/auto_update_base_image.yml
vendored
Normal file
35
.github/workflows/auto_update_base_image.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
name: Regular base image update check
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 5 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
## Sets environment variable
|
||||
DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Docker Image Update Checker
|
||||
id: baseupdatecheck
|
||||
uses: lucacome/docker-image-update-checker@v2.0.0
|
||||
with:
|
||||
base-image: jetty:9.4-jdk11-alpine
|
||||
image: ${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api:latest
|
||||
|
||||
- name: Trigger build_container_develop_branch workflow
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'build_container_develop_branch.yml',
|
||||
ref: 'refs/heads/develop'
|
||||
});
|
||||
if: steps.baseupdatecheck.outputs.needs-updating == 'true'
|
||||
@ -3,6 +3,7 @@ name: Build and publish container develop
|
||||
# read-write repo token
|
||||
# access to secrets
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
@ -28,12 +28,12 @@ jobs:
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Extract branch name
|
||||
shell: bash
|
||||
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}"
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
|
||||
@ -73,7 +73,7 @@ starConnector_supported_types=mapped,internal
|
||||
#endpointMapping.cache.ttl.seconds=0
|
||||
|
||||
## webui props cache time-to-live in seconds
|
||||
#webui.props.cache.ttl.seconds=20
|
||||
#webui.props.cache.ttl.seconds=0
|
||||
|
||||
## DynamicEntity cache time-to-live in seconds, default is 30, the value is 0 at test environment
|
||||
## no 0 value will cause new dynamic entity will be shown after that seconds
|
||||
@ -183,9 +183,9 @@ jwt.use.ssl=false
|
||||
|
||||
|
||||
## Enable writing API metrics (which APIs are called) to RDBMS
|
||||
write_metrics=true
|
||||
write_metrics=false
|
||||
## Enable writing connector metrics (which methods are called)to RDBMS
|
||||
write_connector_metrics=true
|
||||
write_connector_metrics=false
|
||||
|
||||
## ElasticSearch
|
||||
#allow_elasticsearch=true
|
||||
@ -304,7 +304,7 @@ sandbox_data_import_secret=change_me
|
||||
payments_enabled=true
|
||||
|
||||
## Transaction requests are replacing simple payments starting from 1.4.0
|
||||
transactionRequests_enabled=true
|
||||
transactionRequests_enabled=false
|
||||
transactionRequests_connector=mapped
|
||||
|
||||
## Transaction Request Types that are supported on this server. Possible values might include SANDBOX_TAN, COUNTERPARTY, SEPA, FREE_FORM
|
||||
@ -1147,6 +1147,9 @@ default_auth_context_update_request_key=CUSTOMER_NUMBER
|
||||
# the alias prefix path for BerlinGroupV1.3 (OBP built-in is berlin-group/v1.3), the format must be xxx/yyy, eg: 0.6/v1
|
||||
#berlin_group_v1_3_alias_path=
|
||||
|
||||
# Berlin Group URL version
|
||||
#berlin_group_version_1_canonical_path=v1.3
|
||||
|
||||
# Show the path inside of Berlin Group error message
|
||||
#berlin_group_error_message_show_path = true
|
||||
|
||||
@ -1157,12 +1160,18 @@ default_auth_context_update_request_key=CUSTOMER_NUMBER
|
||||
## Berlin Group Create Consent Frequency per Day Upper Limit
|
||||
#berlin_group_frequency_per_day_upper_limit = 4
|
||||
|
||||
## Berlin Group Create Consent ASPSP-SCA-Approach response header value
|
||||
#berlin_group_aspsp_sca_approach = redirect
|
||||
|
||||
# Support multiple brands on one instance. Note this needs checking on a clustered environment
|
||||
#brands_enabled=false
|
||||
|
||||
# Support removing the app type checkbox during consumer registration
|
||||
#consumer_registration.display_app_type=true
|
||||
|
||||
# Default logo URL during of consumer
|
||||
#consumer_default_logo_url=
|
||||
|
||||
# if set this props, we can automatically grant the Entitlements required to use all the Dynamic Endpoint roles belonging
|
||||
# to the bank_ids (Spaces) the User has access to via their validated email domain. Entitlements are generated /refreshed
|
||||
# both following manual login and Direct Login token generation (POST).
|
||||
|
||||
@ -567,6 +567,8 @@ class Boot extends MdcLoggable {
|
||||
Menu.i("Consents") / "consents",
|
||||
Menu.i("Debug") / "debug",
|
||||
Menu.i("debug-basic") / "debug" / "debug-basic",
|
||||
Menu.i("debug-default-header") / "debug" / "debug-default-header",
|
||||
Menu.i("debug-default-footer") / "debug" / "debug-default-footer",
|
||||
Menu.i("debug-localization") / "debug" / "debug-localization",
|
||||
Menu.i("debug-plain") / "debug" / "debug-plain",
|
||||
Menu.i("debug-webui") / "debug" / "debug-webui",
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
package code.api.berlin.group
|
||||
|
||||
import code.api.util.APIUtil
|
||||
import com.openbankproject.commons.util.ApiVersion.berlinGroupV13
|
||||
import com.openbankproject.commons.util.ScannedApiVersion
|
||||
import net.liftweb.common.Full
|
||||
|
||||
object ConstantsBG {
|
||||
val berlinGroupVersion1: ScannedApiVersion = APIUtil.getPropsValue("berlin_group_version_1_canonical_path") match {
|
||||
case Full(props) => berlinGroupV13.copy(apiShortVersion = props)
|
||||
case _ => berlinGroupV13
|
||||
}
|
||||
object SigningBasketsStatus extends Enumeration {
|
||||
type SigningBasketsStatus = Value
|
||||
// Only the codes
|
||||
|
||||
@ -2,6 +2,7 @@ package code.api.builder.AccountInformationServiceAISApi
|
||||
|
||||
import code.api.APIFailureNewStyle
|
||||
import code.api.Constant.{SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID}
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{PostConsentResponseJson, _}
|
||||
import code.api.berlin.group.v1_3.model._
|
||||
import code.api.berlin.group.v1_3.{BgSpecValidation, JSONFactory_BERLIN_GROUP_1_3, JvalueCaseClass}
|
||||
@ -9,8 +10,8 @@ import code.api.util.APIUtil.{passesPsd2Aisp, _}
|
||||
import code.api.util.ApiTag._
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.util.newstyle.BalanceNewStyle
|
||||
import code.api.util._
|
||||
import code.api.util.newstyle.BalanceNewStyle
|
||||
import code.bankconnectors.Connector
|
||||
import code.consent.{ConsentStatus, Consents}
|
||||
import code.context.{ConsentAuthContextProvider, UserAuthContextProvider}
|
||||
@ -22,7 +23,6 @@ import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model._
|
||||
import com.openbankproject.commons.model.enums.{ChallengeType, StrongCustomerAuthenticationStatus, SuppliedAnswerType}
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb
|
||||
import net.liftweb.common.{Empty, Full}
|
||||
import net.liftweb.http.js.JE.JsRaw
|
||||
@ -34,7 +34,7 @@ import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.Future
|
||||
|
||||
object APIMethods_AccountInformationServiceAISApi extends RestHelper {
|
||||
val apiVersion = ApiVersion.berlinGroupV13
|
||||
val apiVersion = ConstantsBG.berlinGroupVersion1
|
||||
val resourceDocs = ArrayBuffer[ResourceDoc]()
|
||||
val apiRelations = ArrayBuffer[ApiRelation]()
|
||||
protected implicit def JvalueToSuper(what: JValue): JvalueCaseClass = JvalueCaseClass(what)
|
||||
@ -156,11 +156,28 @@ recurringIndicator:
|
||||
consentJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
json.extract[PostConsentJson]
|
||||
}
|
||||
_ <- Helper.booleanToFuture(failMsg = BerlinGroupConsentAccessIsEmpty, cc=callContext) {
|
||||
consentJson.access.accounts.isDefined ||
|
||||
consentJson.access.balances.isDefined ||
|
||||
consentJson.access.transactions.isDefined
|
||||
|
||||
_ <- if (consentJson.access.availableAccounts.isDefined) {
|
||||
for {
|
||||
_ <- Helper.booleanToFuture(failMsg = BerlinGroupConsentAccessAvailableAccounts, cc = callContext) {
|
||||
consentJson.access.availableAccounts.contains("allAccounts")
|
||||
}
|
||||
_ <- Helper.booleanToFuture(failMsg = BerlinGroupConsentAccessRecurringIndicator, cc = callContext) {
|
||||
!consentJson.recurringIndicator
|
||||
}
|
||||
_ <- Helper.booleanToFuture(failMsg = BerlinGroupConsentAccessFrequencyPerDay, cc = callContext) {
|
||||
consentJson.frequencyPerDay == 1
|
||||
}
|
||||
} yield Full(())
|
||||
} else {
|
||||
Helper.booleanToFuture(
|
||||
failMsg = BerlinGroupConsentAccessIsEmpty, cc = callContext) {
|
||||
consentJson.access.accounts.isDefined ||
|
||||
consentJson.access.balances.isDefined ||
|
||||
consentJson.access.transactions.isDefined
|
||||
}
|
||||
}
|
||||
|
||||
upperLimit = APIUtil.getPropsAsIntValue("berlin_group_frequency_per_day_upper_limit", 4)
|
||||
_ <- Helper.booleanToFuture(failMsg = FrequencyPerDayError, cc=callContext) {
|
||||
consentJson.frequencyPerDay > 0 && consentJson.frequencyPerDay <= upperLimit
|
||||
@ -242,10 +259,15 @@ recurringIndicator:
|
||||
case "consents" :: consentId :: Nil JsonDelete _ => {
|
||||
cc =>
|
||||
for {
|
||||
(Full(user), callContext) <- authenticatedAccess(cc)
|
||||
(_, callContext) <- applicationAccess(cc)
|
||||
_ <- passesPsd2Aisp(callContext)
|
||||
_ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
|
||||
unboxFullOrFail(_, callContext, ConsentNotFound)
|
||||
consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
|
||||
unboxFullOrFail(_, callContext, ConsentNotFound, 403)
|
||||
}
|
||||
consumerIdFromConsent = consent.mConsumerId.get
|
||||
consumerIdFromCurrentCall = callContext.map(_.consumer.map(_.consumerId.get).getOrElse("None")).getOrElse("None")
|
||||
_ <- Helper.booleanToFuture(failMsg = s"$ConsentNotFound $consumerIdFromConsent != $consumerIdFromCurrentCall", failCode = 403, cc = cc.callContext) {
|
||||
consumerIdFromConsent == consumerIdFromCurrentCall
|
||||
}
|
||||
_ <- Future(Consents.consentProvider.vend.revokeBerlinGroupConsent(consentId)) map {
|
||||
i => connectorEmptyResponse(i, callContext)
|
||||
@ -755,7 +777,7 @@ This method returns the SCA status of a consent initiation's authorisation sub-r
|
||||
(_, callContext) <- authenticatedAccess(cc)
|
||||
_ <- passesPsd2Aisp(callContext)
|
||||
_ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
|
||||
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($consentId)")
|
||||
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($consentId)", 403)
|
||||
}
|
||||
(challenges, callContext) <- NewStyle.function.getChallengesByConsentId(consentId, callContext)
|
||||
} yield {
|
||||
@ -787,10 +809,10 @@ This method returns the SCA status of a consent initiation's authorisation sub-r
|
||||
case "consents" :: consentId:: "status" :: Nil JsonGet _ => {
|
||||
cc =>
|
||||
for {
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
(_, callContext) <- applicationAccess(cc)
|
||||
_ <- passesPsd2Aisp(callContext)
|
||||
consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
|
||||
unboxFullOrFail(_, callContext, ConsentNotFound)
|
||||
unboxFullOrFail(_, callContext, ConsentNotFound, 403)
|
||||
}
|
||||
} yield {
|
||||
val status = consent.status
|
||||
@ -1137,7 +1159,7 @@ using the extended forms as indicated above.
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- passesPsd2Aisp(callContext)
|
||||
consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
|
||||
unboxFullOrFail(_, callContext, ConsentNotFound)
|
||||
unboxFullOrFail(_, callContext, ConsentNotFound, 403)
|
||||
}
|
||||
(challenges, callContext) <- NewStyle.function.createChallengesC2(
|
||||
List(u.userId),
|
||||
@ -1300,7 +1322,7 @@ Maybe in a later version the access path will change.
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- passesPsd2Aisp(callContext)
|
||||
_ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
|
||||
unboxFullOrFail(_, callContext, ConsentNotFound)
|
||||
unboxFullOrFail(_, callContext, ConsentNotFound, 403)
|
||||
}
|
||||
failMsg = s"$InvalidJsonFormat The Json body should be the $TransactionAuthorisation "
|
||||
updateJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
|
||||
|
||||
@ -34,7 +34,7 @@ object BgSpecValidation {
|
||||
|
||||
if (date.isBefore(today)) {
|
||||
Left(s"$InvalidDateFormat The `validUntil` date ($dateStr) cannot be in the past!")
|
||||
} else if (date.isAfter(MaxValidDays)) {
|
||||
} else if (date.isEqual(MaxValidDays) || date.isAfter(MaxValidDays)) {
|
||||
Left(s"$InvalidDateFormat The `validUntil` date ($dateStr) exceeds the maximum allowed period of 180 days (until $MaxValidDays).")
|
||||
} else {
|
||||
Right(date) // Valid date
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package code.api.builder.CommonServicesApi
|
||||
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.berlin.group.v1_3.{JvalueCaseClass, OBP_BERLIN_GROUP_1_3}
|
||||
import code.api.builder.AccountInformationServiceAISApi.APIMethods_AccountInformationServiceAISApi
|
||||
import code.api.builder.PaymentInitiationServicePISApi.APIMethods_PaymentInitiationServicePISApi
|
||||
@ -14,7 +15,7 @@ import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
//TODO maybe we can remove this common services, it just show other apis in this tag. no new ones.
|
||||
object APIMethods_CommonServicesApi extends RestHelper {
|
||||
val apiVersion = ApiVersion.berlinGroupV13
|
||||
val apiVersion = ConstantsBG.berlinGroupVersion1
|
||||
val resourceDocs = ArrayBuffer[ResourceDoc]()
|
||||
val apiRelations = ArrayBuffer[ApiRelation]()
|
||||
val codeContext = CodeContext(resourceDocs, apiRelations)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package code.api.builder.ConfirmationOfFundsServicePIISApi
|
||||
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3._
|
||||
import code.api.berlin.group.v1_3.{JvalueCaseClass, OBP_BERLIN_GROUP_1_3}
|
||||
import code.api.util.APIUtil._
|
||||
@ -20,7 +21,7 @@ import scala.collection.immutable.Nil
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
object APIMethods_ConfirmationOfFundsServicePIISApi extends RestHelper {
|
||||
val apiVersion = ApiVersion.berlinGroupV13
|
||||
val apiVersion = ConstantsBG.berlinGroupVersion1
|
||||
val resourceDocs = ArrayBuffer[ResourceDoc]()
|
||||
val apiRelations = ArrayBuffer[ApiRelation]()
|
||||
protected implicit def JvalueToSuper(what: JValue): JvalueCaseClass = JvalueCaseClass(what)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package code.api.berlin.group.v1_3
|
||||
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.berlin.group.v1_3.model.TransactionStatus.mapTransactionStatus
|
||||
import code.api.berlin.group.v1_3.model._
|
||||
import code.api.util.APIUtil._
|
||||
@ -63,7 +64,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
case class CoreAccountJsonV13(
|
||||
resourceId: String,
|
||||
iban: String,
|
||||
bban: String,
|
||||
bban: Option[String],
|
||||
currency: String,
|
||||
name: String,
|
||||
product: String,
|
||||
@ -138,6 +139,10 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
iban: String,
|
||||
currency : Option[String] = None,
|
||||
)
|
||||
case class FromAccountJson(
|
||||
iban: String,
|
||||
currency : Option[String] = None,
|
||||
)
|
||||
case class TransactionJsonV13(
|
||||
transactionId: String,
|
||||
creditorName: String,
|
||||
@ -191,7 +196,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
)
|
||||
|
||||
case class TransactionsJsonV13(
|
||||
account:FromAccount,
|
||||
account: FromAccountJson,
|
||||
transactions:TransactionsV13Transactions,
|
||||
)
|
||||
|
||||
@ -260,7 +265,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
recurringIndicator: Boolean,
|
||||
validUntil: String,
|
||||
frequencyPerDay: Int,
|
||||
combinedServiceIndicator: Boolean,
|
||||
combinedServiceIndicator: Option[Boolean],
|
||||
lastActionDate: String,
|
||||
consentStatus: String
|
||||
)
|
||||
@ -346,12 +351,12 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
CoreAccountJsonV13(
|
||||
resourceId = x.accountId.value,
|
||||
iban = iBan,
|
||||
bban = bBan,
|
||||
bban = None,
|
||||
currency = x.currency,
|
||||
name = x.name,
|
||||
cashAccountType = cashAccountType,
|
||||
product = x.accountType,
|
||||
balances = accountBalances,
|
||||
balances = if(canReadBalances) accountBalances else None,
|
||||
_links = CoreAccountLinksJsonV13(
|
||||
balances = if(canReadBalances) Some(balanceRef) else None,
|
||||
transactions = if(canReadTransactions) Some(transactionRef) else None,
|
||||
@ -377,7 +382,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
CoreAccountJsonV13(
|
||||
resourceId = x.accountId.value,
|
||||
iban = iBan,
|
||||
bban = bBan,
|
||||
bban = None,
|
||||
currency = x.currency,
|
||||
name = x.name,
|
||||
cashAccountType = x.accountType,
|
||||
@ -454,14 +459,17 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
))
|
||||
}
|
||||
|
||||
def createTransactionJSON(bankAccount: BankAccount, transaction : ModeratedTransaction, creditorAccount: CreditorAccountJson) : TransactionJsonV13 = {
|
||||
val bookingDate = transaction.startDate.getOrElse(null)
|
||||
val valueDate = transaction.finishDate.getOrElse(null)
|
||||
def createTransactionJSON(bankAccount: BankAccount, transaction : ModeratedTransaction) : TransactionJsonV13 = {
|
||||
val bookingDate = transaction.startDate.orNull
|
||||
val valueDate = transaction.finishDate.orNull
|
||||
val creditorName = bankAccount.label
|
||||
TransactionJsonV13(
|
||||
transactionId = transaction.id.value,
|
||||
creditorName = creditorName,
|
||||
creditorAccount = creditorAccount,
|
||||
creditorAccount = CreditorAccountJson(
|
||||
transaction.otherBankAccount.map(_.iban.orNull).orNull,
|
||||
transaction.currency
|
||||
),
|
||||
transactionAmount = AmountOfMoneyV13(APIUtil.stringOptionOrNull(transaction.currency), transaction.amount.get.toString().trim.stripPrefix("-")),
|
||||
bookingDate = BgSpecValidation.formatToISODate(bookingDate) ,
|
||||
valueDate = BgSpecValidation.formatToISODate(valueDate),
|
||||
@ -490,16 +498,19 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
}
|
||||
|
||||
|
||||
def createTransactionFromRequestJSON(bankAccount: BankAccount, transactionRequest : TransactionRequest, creditorAccount: CreditorAccountJson) : TransactionJsonV13 = {
|
||||
def createTransactionFromRequestJSON(bankAccount: BankAccount, tr : TransactionRequest) : TransactionJsonV13 = {
|
||||
val creditorName = bankAccount.accountHolder
|
||||
val remittanceInformationUnstructured = stringOrNull(transactionRequest.body.description)
|
||||
val remittanceInformationUnstructured = stringOrNull(tr.body.description)
|
||||
TransactionJsonV13(
|
||||
transactionId = transactionRequest.id.value,
|
||||
transactionId = tr.id.value,
|
||||
creditorName = creditorName,
|
||||
creditorAccount = creditorAccount,
|
||||
transactionAmount = AmountOfMoneyV13(transactionRequest.charge.value.currency, transactionRequest.charge.value.amount.trim.stripPrefix("-")),
|
||||
bookingDate = BgSpecValidation.formatToISODate(transactionRequest.start_date),
|
||||
valueDate = BgSpecValidation.formatToISODate(transactionRequest.end_date),
|
||||
creditorAccount = CreditorAccountJson(
|
||||
if (tr.other_account_routing_scheme == "IBAN") tr.other_account_routing_address else "",
|
||||
Some(tr.body.value.currency)
|
||||
),
|
||||
transactionAmount = AmountOfMoneyV13(tr.charge.value.currency, tr.charge.value.amount.trim.stripPrefix("-")),
|
||||
bookingDate = BgSpecValidation.formatToISODate(tr.start_date),
|
||||
valueDate = BgSpecValidation.formatToISODate(tr.end_date),
|
||||
remittanceInformationUnstructured = remittanceInformationUnstructured
|
||||
)
|
||||
}
|
||||
@ -508,18 +519,16 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
val accountId = bankAccount.accountId.value
|
||||
val (iban: String, bban: String) = getIbanAndBban(bankAccount)
|
||||
|
||||
val creditorAccount = CreditorAccountJson(
|
||||
val account = FromAccountJson(
|
||||
iban = iban,
|
||||
currency = Some(bankAccount.currency)
|
||||
)
|
||||
TransactionsJsonV13(
|
||||
FromAccount(
|
||||
iban = iban,
|
||||
),
|
||||
account,
|
||||
TransactionsV13Transactions(
|
||||
booked= transactions.map(transaction => createTransactionJSON(bankAccount, transaction, creditorAccount)),
|
||||
pending = transactionRequests.filter(_.status!="COMPLETED").map(transactionRequest => createTransactionFromRequestJSON(bankAccount, transactionRequest, creditorAccount)),
|
||||
_links = TransactionsV13TransactionsLinks(LinkHrefJson(s"/v1.3/accounts/$accountId"))
|
||||
booked= transactions.map(transaction => createTransactionJSON(bankAccount, transaction)),
|
||||
pending = transactionRequests.filter(_.status!="COMPLETED").map(transactionRequest => createTransactionFromRequestJSON(bankAccount, transactionRequest)),
|
||||
_links = TransactionsV13TransactionsLinks(LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/accounts/$accountId"))
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -565,7 +574,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
CardTransactionsV13Transactions(
|
||||
booked= transactions.map(t => createCardTransactionJson(t)),
|
||||
pending = Nil,
|
||||
_links = CardTransactionsLinksV13(LinkHrefJson(s"/v1.3/card-accounts/$accountId"))
|
||||
_links = CardTransactionsLinksV13(LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/card-accounts/$accountId"))
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -576,7 +585,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
consentId = consent.consentId,
|
||||
consentStatus = consent.status.toLowerCase(),
|
||||
_links = ConsentLinksV13(
|
||||
startAuthorisation = Some(Href(s"/v1.3/consents/${consent.consentId}/authorisations"))
|
||||
startAuthorisation = Some(Href(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/consents/${consent.consentId}/authorisations"))
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -595,9 +604,9 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
consentStatus = consent.status.toLowerCase(),
|
||||
_links = ConsentLinksV13(
|
||||
scaRedirect = Some(Href(s"$scaRedirectUrl")),
|
||||
status = Some(Href(s"/v1.3/consents/${consent.consentId}/status")),
|
||||
status = Some(Href(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/consents/${consent.consentId}/status")),
|
||||
// TODO Introduce a working link
|
||||
// scaStatus = Some(Href(s"/v1.3/consents/${consent.consentId}/authorisations/AUTHORISATIONID")),
|
||||
// scaStatus = Some(Href(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/consents/${consent.consentId}/authorisations/AUTHORISATIONID")),
|
||||
)
|
||||
)
|
||||
case Full("redirection_with_dedicated_start_of_authorization") =>
|
||||
@ -607,7 +616,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
consentId = consent.consentId,
|
||||
consentStatus = consent.status.toLowerCase(),
|
||||
_links = ConsentLinksV13(
|
||||
startAuthorisationWithPsuAuthentication = Some(Href(s"/v1.3/consents/${consent.consentId}/authorisations"))
|
||||
startAuthorisationWithPsuAuthentication = Some(Href(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/consents/${consent.consentId}/authorisations"))
|
||||
)
|
||||
)
|
||||
case Full("decoupled") =>
|
||||
@ -615,7 +624,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
consentId = consent.consentId,
|
||||
consentStatus = consent.status.toLowerCase(),
|
||||
_links = ConsentLinksV13(
|
||||
startAuthorisationWithPsuIdentification = Some(Href(s"/v1.3/consents/${consent.consentId}/authorisations"))
|
||||
startAuthorisationWithPsuIdentification = Some(Href(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/consents/${consent.consentId}/authorisations"))
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
@ -626,7 +635,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
def createPutConsentResponseJson(consent: ConsentTrait) : ScaStatusResponse = {
|
||||
ScaStatusResponse(
|
||||
scaStatus = consent.status.toLowerCase(),
|
||||
_links = Some(LinksAll(scaStatus = Some(HrefType(Some(s"/v1.3/consents/${consent.consentId}/authorisations")))))
|
||||
_links = Some(LinksAll(scaStatus = Some(HrefType(Some(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/consents/${consent.consentId}/authorisations")))))
|
||||
)
|
||||
}
|
||||
|
||||
@ -640,7 +649,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
recurringIndicator = createdConsent.recurringIndicator,
|
||||
validUntil = if(createdConsent.validUntil == null) null else new SimpleDateFormat(DateWithDay).format(createdConsent.validUntil),
|
||||
frequencyPerDay = createdConsent.frequencyPerDay,
|
||||
combinedServiceIndicator= createdConsent.combinedServiceIndicator,
|
||||
combinedServiceIndicator = None,
|
||||
lastActionDate = if(createdConsent.lastActionDate == null) null else new SimpleDateFormat(DateWithDay).format(createdConsent.lastActionDate),
|
||||
consentStatus = createdConsent.status.toLowerCase()
|
||||
)
|
||||
@ -651,7 +660,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
scaStatus = challenge.scaStatus.map(_.toString).getOrElse("None"),
|
||||
authorisationId = challenge.authenticationMethodId.getOrElse("None"),
|
||||
pushMessage = "started", //TODO Not implement how to fill this.
|
||||
_links = ScaStatusJsonV13(s"/v1.3/consents/${consent.consentId}/authorisations/${challenge.challengeId}")//TODO, Not sure, what is this for??
|
||||
_links = ScaStatusJsonV13(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/consents/${consent.consentId}/authorisations/${challenge.challengeId}")//TODO, Not sure, what is this for??
|
||||
)
|
||||
}
|
||||
|
||||
@ -704,9 +713,9 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
paymentId = paymentId,
|
||||
_links = InitiatePaymentResponseLinks(
|
||||
scaRedirect = LinkHrefJson(s"$scaRedirectUrl/$paymentId"),
|
||||
self = LinkHrefJson(s"/v1.3/payments/sepa-credit-transfers/$paymentId"),
|
||||
status = LinkHrefJson(s"/v1.3/payments/$paymentId/status"),
|
||||
scaStatus = LinkHrefJson(s"/v1.3/payments/$paymentId/authorisations/${paymentId}")
|
||||
self = LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/payments/sepa-credit-transfers/$paymentId"),
|
||||
status = LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/payments/$paymentId/status"),
|
||||
scaStatus = LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/payments/$paymentId/authorisations/${paymentId}")
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -715,9 +724,9 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
CancelPaymentResponseJson(
|
||||
"ACTC",
|
||||
_links = CancelPaymentResponseLinks(
|
||||
self = LinkHrefJson(s"/v1.3/payments/sepa-credit-transfers/$paymentId"),
|
||||
status = LinkHrefJson(s"/v1.3/payments/sepa-credit-transfers/$paymentId/status"),
|
||||
startAuthorisation = LinkHrefJson(s"/v1.3/payments/sepa-credit-transfers/cancellation-authorisations/${paymentId}")
|
||||
self = LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/payments/sepa-credit-transfers/$paymentId"),
|
||||
status = LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/payments/sepa-credit-transfers/$paymentId/status"),
|
||||
startAuthorisation = LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/payments/sepa-credit-transfers/cancellation-authorisations/${paymentId}")
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -731,7 +740,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
scaStatus = challenge.scaStatus.map(_.toString).getOrElse(""),
|
||||
authorisationId = challenge.challengeId,
|
||||
psuMessage = "Please check your SMS at a mobile device.",
|
||||
_links = ScaStatusJsonV13(s"/v1.3/payments/sepa-credit-transfers/${challenge.challengeId}")
|
||||
_links = ScaStatusJsonV13(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/payments/sepa-credit-transfers/${challenge.challengeId}")
|
||||
)
|
||||
}
|
||||
|
||||
@ -739,7 +748,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
ScaStatusResponse(
|
||||
scaStatus = challenge.scaStatus.map(_.toString).getOrElse(""),
|
||||
psuMessage = Some("Please check your SMS at a mobile device."),
|
||||
_links = Some(LinksAll(scaStatus = Some(HrefType(Some(s"/v1.3/payments/sepa-credit-transfers/${challenge.challengeId}"))))
|
||||
_links = Some(LinksAll(scaStatus = Some(HrefType(Some(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/payments/sepa-credit-transfers/${challenge.challengeId}"))))
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -751,7 +760,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
ScaStatusResponse(
|
||||
scaStatus = challenge.scaStatus.map(_.toString).getOrElse(""),
|
||||
psuMessage = Some("Please check your SMS at a mobile device."),
|
||||
_links = Some(LinksAll(scaStatus = Some(HrefType(Some(s"/v1.3/${paymentService}/${paymentProduct}/${paymentId}/cancellation-authorisations/${challenge.challengeId}"))))
|
||||
_links = Some(LinksAll(scaStatus = Some(HrefType(Some(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/${paymentService}/${paymentProduct}/${paymentId}/cancellation-authorisations/${challenge.challengeId}"))))
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -767,7 +776,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
authorisationId = Some(challenge.challengeId),
|
||||
psuMessage = Some("Please check your SMS at a mobile device."),
|
||||
_links = Some(LinksUpdatePsuAuthentication(
|
||||
scaStatus = Some(HrefType(Some(s"/v1.3/${paymentService}/${paymentProduct}/${paymentId}/cancellation-authorisations/${challenge.challengeId}"))))
|
||||
scaStatus = Some(HrefType(Some(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/${paymentService}/${paymentProduct}/${paymentId}/cancellation-authorisations/${challenge.challengeId}"))))
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -778,7 +787,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
scaStatus = challenge.scaStatus.map(_.toString).getOrElse(""),
|
||||
authorisationId = challenge.challengeId,
|
||||
psuMessage = "Please check your SMS at a mobile device.",
|
||||
_links = ScaStatusJsonV13(s"/v1.3/signing-baskets/${basketId}/authorisations/${challenge.challengeId}")
|
||||
_links = ScaStatusJsonV13(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/signing-baskets/${basketId}/authorisations/${challenge.challengeId}")
|
||||
)
|
||||
}
|
||||
|
||||
@ -787,9 +796,9 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
|
||||
basketId = basket.basketId,
|
||||
transactionStatus = basket.status.toLowerCase(),
|
||||
_links = SigningBasketLinksV13(
|
||||
self = LinkHrefJson(s"/v1.3/signing-baskets/${basket.basketId}"),
|
||||
status = LinkHrefJson(s"/v1.3/signing-baskets/${basket.basketId}/status"),
|
||||
startAuthorisation = LinkHrefJson(s"/v1.3/signing-baskets/${basket.basketId}/authorisations")
|
||||
self = LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/signing-baskets/${basket.basketId}"),
|
||||
status = LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/signing-baskets/${basket.basketId}/status"),
|
||||
startAuthorisation = LinkHrefJson(s"/${ConstantsBG.berlinGroupVersion1.apiShortVersion}/signing-baskets/${basket.basketId}/authorisations")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
package code.api.berlin.group.v1_3
|
||||
|
||||
import code.api.OBPRestHelper
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.builder.AccountInformationServiceAISApi.APIMethods_AccountInformationServiceAISApi
|
||||
import code.api.builder.CommonServicesApi.APIMethods_CommonServicesApi
|
||||
import code.api.builder.ConfirmationOfFundsServicePIISApi.APIMethods_ConfirmationOfFundsServicePIISApi
|
||||
@ -40,7 +41,7 @@ import code.api.builder.SigningBasketsApi.APIMethods_SigningBasketsApi
|
||||
import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints}
|
||||
import code.api.util.ScannedApis
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion,ApiVersionStatus}
|
||||
import com.openbankproject.commons.util.{ApiVersion, ApiVersionStatus, ScannedApiVersion}
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
@ -52,7 +53,7 @@ This file defines which endpoints from all the versions are available in v1
|
||||
*/
|
||||
object OBP_BERLIN_GROUP_1_3 extends OBPRestHelper with MdcLoggable with ScannedApis {
|
||||
|
||||
override val apiVersion = ApiVersion.berlinGroupV13
|
||||
override val apiVersion = ConstantsBG.berlinGroupVersion1
|
||||
val versionStatus = ApiVersionStatus.DRAFT.toString
|
||||
|
||||
val endpoints =
|
||||
|
||||
@ -47,7 +47,7 @@ object OBP_BERLIN_GROUP_1_3_Alias extends OBPRestHelper with MdcLoggable with Sc
|
||||
|
||||
override val allResourceDocs: ArrayBuffer[ResourceDoc] = if(berlinGroupV13AliasPath.nonEmpty){
|
||||
OBP_BERLIN_GROUP_1_3.allResourceDocs.map(resourceDoc => resourceDoc.copy(
|
||||
implementedInApiVersion = apiVersion,
|
||||
implementedInApiVersion = apiVersion.copy(apiStandard = resourceDoc.implementedInApiVersion.apiStandard),
|
||||
))
|
||||
} else ArrayBuffer.empty[ResourceDoc]
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package code.api.builder.PaymentInitiationServicePISApi
|
||||
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{CancelPaymentResponseJson, CancelPaymentResponseLinks, LinkHrefJson, UpdatePaymentPsuDataJson, checkAuthorisationConfirmation, checkSelectPsuAuthenticationMethod, checkTransactionAuthorisation, checkUpdatePsuAuthentication, createCancellationTransactionRequestJson}
|
||||
import code.api.berlin.group.v1_3.model.TransactionStatus.mapTransactionStatus
|
||||
import code.api.berlin.group.v1_3.model._
|
||||
@ -30,7 +31,7 @@ import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.Future
|
||||
|
||||
object APIMethods_PaymentInitiationServicePISApi extends RestHelper {
|
||||
val apiVersion = ApiVersion.berlinGroupV13
|
||||
val apiVersion = ConstantsBG.berlinGroupVersion1
|
||||
val resourceDocs = ArrayBuffer[ResourceDoc]()
|
||||
val apiRelations = ArrayBuffer[ApiRelation]()
|
||||
protected implicit def JvalueToSuper(what: JValue): JvalueCaseClass = JvalueCaseClass(what)
|
||||
|
||||
@ -28,7 +28,7 @@ import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.Future
|
||||
|
||||
object APIMethods_SigningBasketsApi extends RestHelper {
|
||||
val apiVersion = ApiVersion.berlinGroupV13
|
||||
val apiVersion = ConstantsBG.berlinGroupVersion1
|
||||
val resourceDocs = ArrayBuffer[ResourceDoc]()
|
||||
val apiRelations = ArrayBuffer[ApiRelation]()
|
||||
protected implicit def JvalueToSuper(what: JValue): JvalueCaseClass = JvalueCaseClass(what)
|
||||
@ -260,7 +260,7 @@ This method returns the SCA status of a signing basket's authorisation sub-resou
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- passesPsd2Pisp(callContext)
|
||||
_ <- Future(SigningBasketX.signingBasketProvider.vend.getSigningBasketByBasketId(basketId)) map {
|
||||
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($basketId)")
|
||||
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($basketId)", 403)
|
||||
}
|
||||
(challenges, callContext) <- NewStyle.function.getChallengesByBasketId(basketId, callContext)
|
||||
} yield {
|
||||
|
||||
@ -149,6 +149,10 @@ object RequestHeader {
|
||||
final lazy val `PSD2-CERT` = "PSD2-CERT"
|
||||
final lazy val `If-None-Match` = "If-None-Match"
|
||||
|
||||
final lazy val `PSU-Geo-Location` = "PSU-Geo-Location" // Berlin Group
|
||||
final lazy val `PSU-Device-Name` = "PSU-Device-Name" // Berlin Group
|
||||
final lazy val `PSU-Device-ID` = "PSU-Device-ID" // Berlin Group
|
||||
final lazy val `PSU-IP-Address` = "PSU-IP-Address" // Berlin Group
|
||||
final lazy val `X-Request-ID` = "X-Request-ID" // Berlin Group
|
||||
final lazy val `TPP-Redirect-URI` = "TPP-Redirect-URI" // Berlin Group
|
||||
final lazy val `TPP-Nok-Redirect-URI` = "TPP-Nok-Redirect-URI" // Redirect URI in case of an error.
|
||||
@ -171,6 +175,7 @@ object RequestHeader {
|
||||
final lazy val `If-Modified-Since` = "If-Modified-Since"
|
||||
}
|
||||
object ResponseHeader {
|
||||
final lazy val `ASPSP-SCA-Approach` = "ASPSP-SCA-Approach" // Berlin Group
|
||||
final lazy val `Correlation-Id` = "Correlation-Id"
|
||||
final lazy val `WWW-Authenticate` = "WWW-Authenticate"
|
||||
final lazy val ETag = "ETag"
|
||||
|
||||
@ -34,6 +34,7 @@ import code.api.OAuthHandshake._
|
||||
import code.api.UKOpenBanking.v2_0_0.OBP_UKOpenBanking_200
|
||||
import code.api.UKOpenBanking.v3_1_0.OBP_UKOpenBanking_310
|
||||
import code.api._
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{ErrorMessageBG, ErrorMessagesBG}
|
||||
import code.api.cache.Caching
|
||||
import code.api.dynamic.endpoint.OBPAPIDynamicEndpoint
|
||||
@ -448,8 +449,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
|
||||
private def getHeadersNewStyle(cc: Option[CallContextLight]) = {
|
||||
CustomResponseHeaders(
|
||||
getGatewayLoginHeader(cc).list :::
|
||||
getRateLimitHeadersNewStyle(cc).list :::
|
||||
getGatewayLoginHeader(cc).list :::
|
||||
getRequestHeadersBerlinGroup(cc).list :::
|
||||
getRateLimitHeadersNewStyle(cc).list :::
|
||||
getPaginationHeadersNewStyle(cc).list :::
|
||||
getRequestHeadersToMirror(cc).list :::
|
||||
getRequestHeadersToEcho(cc).list
|
||||
@ -520,7 +522,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
val mirrorByProperties = getPropsValue("mirror_request_headers_to_response", "").split(",").toList.map(_.trim)
|
||||
|
||||
val mirrorRequestHeadersToResponse: List[String] =
|
||||
if (callContext.exists(_.url.contains(ApiVersion.berlinGroupV13.urlPrefix))) {
|
||||
if (callContext.exists(_.url.contains(ConstantsBG.berlinGroupVersion1.urlPrefix))) {
|
||||
// Berlin Group Specification
|
||||
RequestHeader.`X-Request-ID` :: mirrorByProperties
|
||||
} else {
|
||||
@ -555,6 +557,18 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
CustomResponseHeaders(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
def getRequestHeadersBerlinGroup(callContext: Option[CallContextLight]): CustomResponseHeaders = {
|
||||
val aspspScaApproach = getPropsValue("berlin_group_aspsp_sca_approach", defaultValue = "redirect")
|
||||
callContext match {
|
||||
case Some(cc) if cc.url.contains(ConstantsBG.berlinGroupVersion1.urlPrefix) && cc.url.endsWith("/consents") =>
|
||||
CustomResponseHeaders(List(
|
||||
(ResponseHeader.`ASPSP-SCA-Approach`, aspspScaApproach)
|
||||
))
|
||||
case _ =>
|
||||
CustomResponseHeaders(Nil)
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param jwt is a JWT value extracted from GatewayLogin Authorization Header.
|
||||
@ -725,7 +739,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
}
|
||||
def composeErrorMessage() = {
|
||||
val path = callContextLight.map(_.url).getOrElse("")
|
||||
if (path.contains(ApiVersion.berlinGroupV13.urlPrefix)) {
|
||||
if (path.contains(ConstantsBG.berlinGroupVersion1.urlPrefix)) {
|
||||
val path =
|
||||
if(APIUtil.getPropsAsBoolValue("berlin_group_error_message_show_path", defaultValue = true))
|
||||
callContextLight.map(_.url)
|
||||
@ -3001,12 +3015,12 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
val res =
|
||||
if (authHeadersWithEmptyValues.nonEmpty) { // Check Authorization Headers Empty Values
|
||||
val message = ErrorMessages.EmptyRequestHeaders + s"Header names: ${authHeadersWithEmptyValues.mkString(", ")}"
|
||||
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), None) }
|
||||
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), Some(cc)) }
|
||||
} else if (authHeadersWithEmptyNames.nonEmpty) { // Check Authorization Headers Empty Names
|
||||
val message = ErrorMessages.EmptyRequestHeaders + s"Header values: ${authHeadersWithEmptyNames.mkString(", ")}"
|
||||
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), None) }
|
||||
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), Some(cc)) }
|
||||
} else if (authHeaders.size > 1) { // Check Authorization Headers ambiguity
|
||||
Future { (Failure(ErrorMessages.AuthorizationHeaderAmbiguity + s"${authHeaders}"), None) }
|
||||
Future { (Failure(ErrorMessages.AuthorizationHeaderAmbiguity + s"${authHeaders}"), Some(cc)) }
|
||||
} else if (APIUtil.`hasConsent-ID`(reqHeaders)) { // Berlin Group's Consent
|
||||
Consent.applyBerlinGroupRules(APIUtil.`getConsent-ID`(reqHeaders), cc.copy(consumer = consumerByCertificate))
|
||||
} else if (APIUtil.hasConsentJWT(reqHeaders)) { // Open Bank Project's Consent
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package code.api.util
|
||||
|
||||
import code.api.APIFailureNewStyle
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.{APIFailureNewStyle, RequestHeader}
|
||||
import code.api.util.APIUtil.{OBPReturnType, fullBoxOrException}
|
||||
import code.api.util.BerlinGroupSigning.getHeaderValue
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.model.User
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
@ -27,7 +29,7 @@ object BerlinGroupCheck extends MdcLoggable {
|
||||
|
||||
private def validateHeaders(verb: String, url: String, reqHeaders: List[HTTPParam], forwardResult: (Box[User], Option[CallContext])): (Box[User], Option[CallContext]) = {
|
||||
val headerMap = reqHeaders.map(h => h.name.toLowerCase -> h).toMap
|
||||
val missingHeaders = if(url.contains(ApiVersion.berlinGroupV13.urlPrefix) && url.endsWith("/consents"))
|
||||
val missingHeaders = if(url.contains(ConstantsBG.berlinGroupVersion1.urlPrefix) && url.endsWith("/consents"))
|
||||
(berlinGroupMandatoryHeaders ++ berlinGroupMandatoryHeaderConsent).filterNot(headerMap.contains)
|
||||
else
|
||||
berlinGroupMandatoryHeaders.filterNot(headerMap.contains)
|
||||
@ -43,8 +45,22 @@ object BerlinGroupCheck extends MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
def isTppRequestsWithoutPsuInvolvement(requestHeaders: List[HTTPParam]): Boolean = {
|
||||
val psuIpAddress = getHeaderValue(RequestHeader.`PSU-IP-Address`, requestHeaders)
|
||||
val psuDeviceId = getHeaderValue(RequestHeader.`PSU-Device-ID`, requestHeaders)
|
||||
val psuDeviceNAme = getHeaderValue(RequestHeader.`PSU-Device-Name`, requestHeaders)
|
||||
if(psuIpAddress == "0.0.0.0" || psuDeviceId == "no-psu-involved" || psuDeviceNAme == "no-psu-involved") {
|
||||
logger.debug(s"isTppRequestsWithoutPsuInvolvement.psuIpAddress: $psuIpAddress")
|
||||
logger.debug(s"isTppRequestsWithoutPsuInvolvement.psuDeviceId: $psuDeviceId")
|
||||
logger.debug(s"isTppRequestsWithoutPsuInvolvement.psuDeviceNAme: $psuDeviceNAme")
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def validate(body: Box[String], verb: String, url: String, reqHeaders: List[HTTPParam], forwardResult: (Box[User], Option[CallContext])): OBPReturnType[Box[User]] = {
|
||||
if(url.contains(ApiVersion.berlinGroupV13.urlPrefix)) {
|
||||
if(url.contains(ConstantsBG.berlinGroupVersion1.urlPrefix)) {
|
||||
validateHeaders(verb, url, reqHeaders, forwardResult) match {
|
||||
case (user, _) if user.isDefined || user == Empty => // All good. Chain another check
|
||||
// Verify signed request (Berlin Group)
|
||||
|
||||
@ -69,8 +69,6 @@ object BerlinGroupError {
|
||||
case "401" if message.contains("OBP-35018") => "CONSENT_INVALID"
|
||||
case "401" if message.contains("OBP-35005") => "CONSENT_INVALID"
|
||||
|
||||
case "403" if message.contains("OBP-35001") => "CONSENT_UNKNOWN"
|
||||
|
||||
case "401" if message.contains("OBP-20300") => "CERTIFICATE_BLOCKED"
|
||||
case "401" if message.contains("OBP-20312") => "CERTIFICATE_INVALID"
|
||||
case "401" if message.contains("OBP-20300") => "CERTIFICATE_INVALID"
|
||||
@ -80,6 +78,7 @@ object BerlinGroupError {
|
||||
|
||||
case "400" if message.contains("OBP-35018") => "CONSENT_UNKNOWN"
|
||||
case "400" if message.contains("OBP-35001") => "CONSENT_UNKNOWN"
|
||||
case "403" if message.contains("OBP-35001") => "CONSENT_UNKNOWN"
|
||||
|
||||
case "404" if message.contains("OBP-30076") => "RESOURCE_UNKNOWN"
|
||||
|
||||
@ -91,6 +90,9 @@ object BerlinGroupError {
|
||||
case "400" if message.contains("OBP-20252") => "FORMAT_ERROR"
|
||||
case "400" if message.contains("OBP-20251") => "FORMAT_ERROR"
|
||||
case "400" if message.contains("OBP-20088") => "FORMAT_ERROR"
|
||||
case "400" if message.contains("OBP-20089") => "FORMAT_ERROR"
|
||||
case "400" if message.contains("OBP-20090") => "FORMAT_ERROR"
|
||||
case "400" if message.contains("OBP-20091") => "FORMAT_ERROR"
|
||||
|
||||
case "429" if message.contains("OBP-10018") => "ACCESS_EXCEEDED"
|
||||
case _ => code
|
||||
|
||||
@ -160,14 +160,15 @@ object BerlinGroupSigning extends MdcLoggable {
|
||||
val certificate = getCertificateFromTppSignatureCertificate(requestHeaders)
|
||||
X509.validateCertificate(certificate) match {
|
||||
case Full(true) => // PEM certificate is ok
|
||||
val digest = generateDigest(body.getOrElse(""))
|
||||
if(digest == getHeaderValue(RequestHeader.Digest, requestHeaders)) { // Verifying the Hash in the Digest Field
|
||||
val generatedDigest = generateDigest(body.getOrElse(""))
|
||||
val requestHeaderDigest = getHeaderValue(RequestHeader.Digest, requestHeaders)
|
||||
if(generatedDigest == requestHeaderDigest) { // Verifying the Hash in the Digest Field
|
||||
val signatureHeaderValue = getHeaderValue(RequestHeader.Signature, requestHeaders)
|
||||
val signature = parseSignatureHeader(signatureHeaderValue).getOrElse("signature", "NONE")
|
||||
val headersToSign = parseSignatureHeader(signatureHeaderValue).getOrElse("headers", "").split(" ").toList
|
||||
val headers = headersToSign.map(h =>
|
||||
if (h.toLowerCase() == RequestHeader.Digest.toLowerCase()) {
|
||||
s"$h: $digest"
|
||||
s"$h: $generatedDigest"
|
||||
} else {
|
||||
s"$h: ${getHeaderValue(h, requestHeaders)}"
|
||||
}
|
||||
@ -183,6 +184,8 @@ object BerlinGroupSigning extends MdcLoggable {
|
||||
case (false, _) => (Failure(ErrorMessages.X509PublicKeyCannotVerify), forwardResult._2)
|
||||
}
|
||||
} else { // The two DIGEST hashes do NOT match, the integrity of the request body is NOT confirmed.
|
||||
logger.debug(s"Generated digest: $generatedDigest")
|
||||
logger.debug(s"Request header digest: $requestHeaderDigest")
|
||||
(Failure(ErrorMessages.X509PublicKeyCannotVerify), forwardResult._2)
|
||||
}
|
||||
case Failure(msg, t, c) => (Failure(msg, t, c), forwardResult._2) // PEM certificate is not valid
|
||||
@ -298,7 +301,8 @@ object BerlinGroupSigning extends MdcLoggable {
|
||||
developerEmail = extractedEmail,
|
||||
redirectURL = None,
|
||||
createdByUserId = None,
|
||||
certificate = None
|
||||
certificate = None,
|
||||
logoUrl = APIUtil.getPropsValue("consumer_default_logo_url")
|
||||
)
|
||||
|
||||
// Set or update certificate
|
||||
|
||||
@ -1,21 +1,28 @@
|
||||
package code.api.util
|
||||
|
||||
import code.accountholders.AccountHolders
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.{Date, UUID}
|
||||
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{ConsentAccessJson, PostConsentJson}
|
||||
import code.api.util.APIUtil.fullBoxOrException
|
||||
import code.api.util.ApiRole.{canCreateEntitlementAtAnyBank, canCreateEntitlementAtOneBank}
|
||||
import code.api.util.BerlinGroupSigning.getHeaderValue
|
||||
import code.api.util.ErrorMessages.{CouldNotAssignAccountAccess, InvalidConnectorResponse, NoViewReadAccountsBerlinGroup}
|
||||
import code.api.v3_1_0.{PostConsentBodyCommonJson, PostConsentEntitlementJsonV310, PostConsentViewJsonV310}
|
||||
import code.api.v5_0_0.HelperInfoJson
|
||||
import code.api.{APIFailure, Constant, RequestHeader}
|
||||
import code.api.{APIFailure, APIFailureNewStyle, Constant, RequestHeader}
|
||||
import code.bankconnectors.Connector
|
||||
import code.consent
|
||||
import code.consent.ConsentStatus.ConsentStatus
|
||||
import code.consent.{ConsentStatus, Consents, MappedConsent}
|
||||
import code.consumer.Consumers
|
||||
import code.context.{ConsentAuthContextProvider, UserAuthContextProvider}
|
||||
import code.entitlement.Entitlement
|
||||
import code.model.Consumer
|
||||
import code.model.dataAccess.BankAccountRouting
|
||||
import code.scheduler.ConsentScheduler.logger
|
||||
import code.users.Users
|
||||
import code.util.Helper.MdcLoggable
|
||||
import code.util.HydraUtil
|
||||
@ -253,7 +260,7 @@ object Consent extends MdcLoggable {
|
||||
case false =>
|
||||
Failure(ErrorMessages.ConsentVerificationIssue)
|
||||
}
|
||||
case Full(c) if c.apiStandard == ApiVersion.berlinGroupV13.apiStandard && // Berlin Group Consent
|
||||
case Full(c) if c.apiStandard == ConstantsBG.berlinGroupVersion1.apiStandard && // Berlin Group Consent
|
||||
c.status.toLowerCase() != ConsentStatus.valid.toString =>
|
||||
Failure(s"${ErrorMessages.ConsentStatusIssue}${ConsentStatus.valid.toString}.")
|
||||
case Full(c) if c.mStatus.toString().toUpperCase() != ConsentStatus.ACCEPTED.toString =>
|
||||
@ -566,9 +573,11 @@ object Consent extends MdcLoggable {
|
||||
logger.debug(s"End of net.liftweb.json.parse(jsonAsString).extract[ConsentJWT].checkConsent.consentBox: $consent")
|
||||
consentBox match { // Check is it Consent-JWT expired
|
||||
case (Full(true)) => // OK
|
||||
// Update MappedConsent.usesSoFarTodayCounter field
|
||||
val consentUpdatedBox = Consents.consentProvider.vend.updateBerlinGroupConsent(consentId, currentCounterState + 1)
|
||||
logger.debug(s"applyBerlinGroupConsentRulesCommon.consentUpdatedBox: $consentUpdatedBox")
|
||||
if(BerlinGroupCheck.isTppRequestsWithoutPsuInvolvement(callContext.requestHeaders)) {
|
||||
// Update MappedConsent.usesSoFarTodayCounter field
|
||||
val consentUpdatedBox = Consents.consentProvider.vend.updateBerlinGroupConsent(consentId, currentCounterState + 1)
|
||||
logger.debug(s"applyBerlinGroupConsentRulesCommon.consentUpdatedBox: $consentUpdatedBox")
|
||||
}
|
||||
applyConsentRules(consent, updatedCallContext)
|
||||
case failure@Failure(_, _, _) => // Handled errors
|
||||
Future(failure, Some(updatedCallContext))
|
||||
@ -595,12 +604,14 @@ object Consent extends MdcLoggable {
|
||||
Future(Failure("Cannot extract data from: " + consentId), Some(updatedCallContext))
|
||||
}
|
||||
} else {
|
||||
Future(Failure(ErrorMessages.TooManyRequests + s" ${RequestHeader.`Consent-ID`}: $consentId"), Some(updatedCallContext))
|
||||
val errorMessage = ErrorMessages.TooManyRequests + s" ${RequestHeader.`Consent-ID`}: $consentId"
|
||||
Future(fullBoxOrException(Empty ~> APIFailureNewStyle(errorMessage, 429, Some(callContext.toLight))), Some(callContext))
|
||||
}
|
||||
case failure@Failure(_, _, _) =>
|
||||
Future(failure, Some(callContext))
|
||||
case _ =>
|
||||
Future(Failure(ErrorMessages.ConsentNotFound + s" ($consentId)"), Some(callContext))
|
||||
val errorMessage = ErrorMessages.ConsentNotFound + s" ($consentId)"
|
||||
Future(fullBoxOrException(Empty ~> APIFailureNewStyle(errorMessage, 400, Some(callContext.toLight))), Some(callContext))
|
||||
}
|
||||
}
|
||||
def applyBerlinGroupRules(consentId: Option[String], callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
|
||||
@ -772,6 +783,10 @@ object Consent extends MdcLoggable {
|
||||
}
|
||||
val tppRedirectUri: Option[HTTPParam] = callContext.map(_.requestHeaders).getOrElse(Nil).find(_.name == RequestHeader.`TPP-Redirect-URI`)
|
||||
val tppNokRedirectUri: Option[HTTPParam] = callContext.map(_.requestHeaders).getOrElse(Nil).find(_.name == RequestHeader.`TPP-Nok-Redirect-URI`)
|
||||
val xRequestId: Option[HTTPParam] = callContext.map(_.requestHeaders).getOrElse(Nil).find(_.name == RequestHeader.`X-Request-ID`)
|
||||
val psuDeviceId: Option[HTTPParam] = callContext.map(_.requestHeaders).getOrElse(Nil).find(_.name == RequestHeader.`PSU-Device-ID`)
|
||||
val psuIpAddress: Option[HTTPParam] = callContext.map(_.requestHeaders).getOrElse(Nil).find(_.name == RequestHeader.`PSU-IP-Address`)
|
||||
val psuGeoLocation: Option[HTTPParam] = callContext.map(_.requestHeaders).getOrElse(Nil).find(_.name == RequestHeader.`PSU-Geo-Location`)
|
||||
Future.sequence(accounts ::: balances ::: transactions) map { views =>
|
||||
val json = ConsentJWT(
|
||||
createdByUserId = user.map(_.userId).getOrElse(""),
|
||||
@ -782,7 +797,12 @@ object Consent extends MdcLoggable {
|
||||
iat = currentTimeInSeconds,
|
||||
nbf = currentTimeInSeconds,
|
||||
exp = validUntilTimeInSeconds,
|
||||
request_headers = tppRedirectUri.toList ::: tppNokRedirectUri.toList,
|
||||
request_headers = tppRedirectUri.toList :::
|
||||
tppNokRedirectUri.toList :::
|
||||
xRequestId.toList :::
|
||||
psuDeviceId.toList :::
|
||||
psuIpAddress.toList :::
|
||||
psuGeoLocation.toList,
|
||||
name = None,
|
||||
email = None,
|
||||
entitlements = Nil,
|
||||
@ -855,19 +875,78 @@ object Consent extends MdcLoggable {
|
||||
}
|
||||
}
|
||||
}
|
||||
def updateUserIdOfBerlinGroupConsentJWT(createdByUserId: String,
|
||||
consent: MappedConsent,
|
||||
callContext: Option[CallContext]): Future[Box[String]] = {
|
||||
def updateViewsOfBerlinGroupConsentJWT(user: User,
|
||||
consent: MappedConsent,
|
||||
callContext: Option[CallContext]): Future[Box[MappedConsent]] = {
|
||||
implicit val dateFormats = CustomJsonFormats.formats
|
||||
val payloadToUpdate: Box[ConsentJWT] = JwtUtil.getSignedPayloadAsJson(consent.jsonWebToken) // Payload as JSON string
|
||||
.map(net.liftweb.json.parse(_).extract[ConsentJWT]) // Extract case class
|
||||
|
||||
Future {
|
||||
val updatedPayload = payloadToUpdate.map(i => i.copy(createdByUserId = createdByUserId)) // Update only the field "createdByUserId"
|
||||
val jwtPayloadAsJson = compactRender(Extraction.decompose(updatedPayload))
|
||||
val jwtClaims: JWTClaimsSet = JWTClaimsSet.parse(jwtPayloadAsJson)
|
||||
Full(CertificateUtil.jwtWithHmacProtection(jwtClaims, consent.secret))
|
||||
val availableAccountsUserIbans: List[String] = payloadToUpdate match {
|
||||
case Full(consentJwt) =>
|
||||
val availableAccountsUserIbans: List[String] =
|
||||
if (consentJwt.access.map(_.availableAccounts.contains("allAccounts")).isDefined) {
|
||||
// Get all accounts held by the current user
|
||||
val userAccounts: List[BankIdAccountId] =
|
||||
AccountHolders.accountHolders.vend.getAccountsHeldByUser(user, Some(null)).toList
|
||||
userAccounts.flatMap { acc =>
|
||||
BankAccountRouting.find(
|
||||
By(BankAccountRouting.BankId, acc.bankId.value),
|
||||
By(BankAccountRouting.AccountId, acc.accountId.value),
|
||||
By(BankAccountRouting.AccountRoutingScheme, "IBAN")
|
||||
).map(_.AccountRoutingAddress.get)
|
||||
}
|
||||
} else {
|
||||
val emptyList: List[String] = Nil
|
||||
emptyList
|
||||
}
|
||||
availableAccountsUserIbans
|
||||
case _ =>
|
||||
val emptyList: List[String] = Nil
|
||||
emptyList
|
||||
}
|
||||
|
||||
|
||||
// 1. Add access
|
||||
val availableAccounts: List[Future[ConsentView]] = availableAccountsUserIbans.distinct map { iban =>
|
||||
Connector.connector.vend.getBankAccountByIban(iban, callContext) map { bankAccount =>
|
||||
logger.debug(s"createBerlinGroupConsentJWT.accounts.bankAccount: $bankAccount")
|
||||
val error = s"${InvalidConnectorResponse} IBAN: ${iban} ${handleBox(bankAccount._1)}"
|
||||
ConsentView(
|
||||
bank_id = bankAccount._1.map(_.bankId.value).getOrElse(""),
|
||||
account_id = bankAccount._1.map(_.accountId.value).openOrThrowException(error),
|
||||
view_id = Constant.SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID,
|
||||
None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Future.sequence(availableAccounts) map { views =>
|
||||
if(views.isEmpty) {
|
||||
Empty
|
||||
} else {
|
||||
val updatedPayload = payloadToUpdate.map(i =>
|
||||
i.copy(views = views) // Update the field "views"
|
||||
)
|
||||
val jwtPayloadAsJson = compactRender(Extraction.decompose(updatedPayload))
|
||||
val jwtClaims: JWTClaimsSet = JWTClaimsSet.parse(jwtPayloadAsJson)
|
||||
val jwt = CertificateUtil.jwtWithHmacProtection(jwtClaims, consent.secret)
|
||||
Consents.consentProvider.vend.setJsonWebToken(consent.consentId, jwt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def updateUserIdOfBerlinGroupConsentJWT(createdByUserId: String,
|
||||
consent: MappedConsent,
|
||||
callContext: Option[CallContext]): Box[String] = {
|
||||
implicit val dateFormats = CustomJsonFormats.formats
|
||||
val payloadToUpdate: Box[ConsentJWT] = JwtUtil.getSignedPayloadAsJson(consent.jsonWebToken) // Payload as JSON string
|
||||
.map(net.liftweb.json.parse(_).extract[ConsentJWT]) // Extract case class
|
||||
|
||||
val updatedPayload = payloadToUpdate.map(i => i.copy(createdByUserId = createdByUserId)) // Update only the field "createdByUserId"
|
||||
val jwtPayloadAsJson = compactRender(Extraction.decompose(updatedPayload))
|
||||
val jwtClaims: JWTClaimsSet = JWTClaimsSet.parse(jwtPayloadAsJson)
|
||||
Full(CertificateUtil.jwtWithHmacProtection(jwtClaims, consent.secret))
|
||||
}
|
||||
|
||||
def createUKConsentJWT(
|
||||
@ -1036,6 +1115,26 @@ object Consent extends MdcLoggable {
|
||||
consentsOfBank
|
||||
}
|
||||
|
||||
def expireAllPreviousValidBerlinGroupConsents(consent: MappedConsent, updateTostatus: ConsentStatus): Boolean = {
|
||||
if(updateTostatus == ConsentStatus.valid &&
|
||||
consent.apiStandard == ConstantsBG.berlinGroupVersion1.apiStandard) {
|
||||
MappedConsent.findAll( // Find all
|
||||
By(MappedConsent.mApiStandard, ConstantsBG.berlinGroupVersion1.apiStandard), // Berlin Group
|
||||
By(MappedConsent.mRecurringIndicator, true), // recurring
|
||||
By(MappedConsent.mStatus, ConsentStatus.valid.toString), // and valid consents
|
||||
By(MappedConsent.mUserId, consent.userId), // for the same PSU
|
||||
By(MappedConsent.mConsumerId, consent.consumerId), // from the same TPP
|
||||
).filterNot(_.consentId == consent.consentId) // Exclude current consent
|
||||
.map{ c => // Set to expired
|
||||
val changedStatus = c.mStatus(ConsentStatus.expired.toString).mLastActionDate(new Date()).save
|
||||
if(changedStatus) logger.warn(s"|---> Changed status to ${ConsentStatus.expired.toString} for consent ID: ${c.id}")
|
||||
changedStatus
|
||||
}.forall(_ == true)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Example Usage
|
||||
val box1: Box[String] = Full("Hello, World!")
|
||||
|
||||
@ -244,7 +244,10 @@ object ErrorMessages {
|
||||
s"OBP-20087: The current source view.can_revoke_access_to_custom_views is false."
|
||||
|
||||
val BerlinGroupConsentAccessIsEmpty = s"OBP-20088: An access must be requested."
|
||||
|
||||
val BerlinGroupConsentAccessRecurringIndicator = s"OBP-20089: Recurring indicator must be false when availableAccounts is used."
|
||||
val BerlinGroupConsentAccessFrequencyPerDay = s"OBP-20090: Frequency per day must be 1 when availableAccounts is used."
|
||||
val BerlinGroupConsentAccessAvailableAccounts = s"OBP-20091: availableAccounts must be exactly 'allAccounts'."
|
||||
|
||||
val UserNotSuperAdminOrMissRole = "OBP-20101: Current User is not super admin or is missing entitlements:"
|
||||
val CannotGetOrCreateUser = "OBP-20102: Cannot get or create user."
|
||||
val InvalidUserProvider = "OBP-20103: Invalid DAuth User Provider."
|
||||
@ -268,6 +271,7 @@ object ErrorMessages {
|
||||
val OneTimePasswordExpired = "OBP-20211: The One Time Password (OTP) has expired. "
|
||||
val Oauth2IsNotRecognized = "OBP-20214: OAuth2 Access Token is not recognised at this instance."
|
||||
val Oauth2ValidateAccessTokenError = "OBP-20215: There was a problem validating the OAuth2 access token. "
|
||||
val OneTimePasswordInvalid = "OBP-20216: The One Time Password (OTP) is invalid. "
|
||||
|
||||
val AuthorizationHeaderAmbiguity = "OBP-20250: Request headers used for authorization are ambiguous. "
|
||||
val MissingMandatoryBerlinGroupHeaders= "OBP-20251: Missing mandatory request headers. "
|
||||
|
||||
@ -2,6 +2,7 @@ package code.api.util
|
||||
|
||||
import java.math.BigInteger
|
||||
import net.liftweb.common.Box
|
||||
import org.iban4j.IbanUtil
|
||||
|
||||
object HashUtil {
|
||||
def Sha256Hash(in: String): String = {
|
||||
@ -25,5 +26,8 @@ object HashUtil {
|
||||
val hashedText = Sha256Hash(plainText)
|
||||
println("Password: " + plainText)
|
||||
println("Hashed password: " + hashedText)
|
||||
println("BBAN: " + IbanUtil.getBban("AT483200000012345864"))
|
||||
println("Bank code: " + IbanUtil.getBankCode("AT483200000012345864"))
|
||||
println("Country code: " + IbanUtil.getCountryCode("AT483200000012345864"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -8081,7 +8081,9 @@ trait APIMethods400 extends MdcLoggable {
|
||||
consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
|
||||
i => connectorEmptyResponse(i, callContext)
|
||||
}
|
||||
_ <- Helper.booleanToFuture(ConsentUserAlreadyAdded, cc=callContext) { consent.userId != null }
|
||||
_ <- Helper.booleanToFuture(ConsentUserAlreadyAdded, cc = cc.callContext) {
|
||||
Option(consent.userId).forall(_.isBlank) // checks whether userId is not populated
|
||||
}
|
||||
consent <- Future(Consents.consentProvider.vend.updateConsentUser(consentId, user)) map {
|
||||
i => connectorEmptyResponse(i, callContext)
|
||||
}
|
||||
|
||||
@ -1439,7 +1439,7 @@ trait APIMethods510 {
|
||||
nameOf(updateConsentUserIdByConsentId),
|
||||
"PUT",
|
||||
"/management/banks/BANK_ID/consents/CONSENT_ID/created-by-user",
|
||||
"Update Consent Created by User by CONSENT_ID",
|
||||
"Update Created by User of Consent by CONSENT_ID",
|
||||
s"""
|
||||
|
|
||||
|This endpoint is used to Update the User bound to a consent.
|
||||
@ -1493,11 +1493,11 @@ trait APIMethods510 {
|
||||
consent <- Future(Consents.consentProvider.vend.updateConsentUser(consentId, user)) map {
|
||||
i => connectorEmptyResponse(i, cc.callContext)
|
||||
}
|
||||
consentJWT <- Consent.updateUserIdOfBerlinGroupConsentJWT(
|
||||
consentJWT <- Future(Consent.updateUserIdOfBerlinGroupConsentJWT(
|
||||
consentJson.user_id,
|
||||
consent,
|
||||
cc.callContext
|
||||
) map {
|
||||
)) map {
|
||||
i => connectorEmptyResponse(i, cc.callContext)
|
||||
}
|
||||
updatedConsent <- Future(Consents.consentProvider.vend.setJsonWebToken(consent.consentId, consentJWT)) map {
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
package code.api.v5_1_0
|
||||
|
||||
import code.api.Constant
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.ConsentAccessJson
|
||||
import code.api.util.APIUtil.{DateWithDay, DateWithSeconds, gitCommit, stringOrNull}
|
||||
import code.api.util._
|
||||
@ -943,8 +944,8 @@ object JSONFactory510 extends CustomJsonFormats {
|
||||
last_action_date = if (c.lastActionDate != null) new SimpleDateFormat(DateWithDay).format(c.lastActionDate) else null,
|
||||
last_usage_date = if (c.usesSoFarTodayCounterUpdatedAt != null) new SimpleDateFormat(DateWithSeconds).format(c.usesSoFarTodayCounterUpdatedAt) else null,
|
||||
jwt_payload = jwtPayload,
|
||||
frequency_per_day = if(c.apiStandard == ApiVersion.berlinGroupV13.apiStandard) Some(c.frequencyPerDay) else None,
|
||||
remaining_requests = if(c.apiStandard == ApiVersion.berlinGroupV13.apiStandard) Some(c.frequencyPerDay - c.usesSoFarTodayCounter) else None,
|
||||
frequency_per_day = if(c.apiStandard == ConstantsBG.berlinGroupVersion1.apiStandard) Some(c.frequencyPerDay) else None,
|
||||
remaining_requests = if(c.apiStandard == ConstantsBG.berlinGroupVersion1.apiStandard) Some(c.frequencyPerDay - c.usesSoFarTodayCounter) else None,
|
||||
api_standard = c.apiStandard,
|
||||
api_version = c.apiVersion
|
||||
)
|
||||
|
||||
154
obp-api/src/main/scala/code/cardano/cardano.scala
Normal file
154
obp-api/src/main/scala/code/cardano/cardano.scala
Normal file
@ -0,0 +1,154 @@
|
||||
import java.io.{File, PrintWriter}
|
||||
import scala.sys.process._
|
||||
import java.security.MessageDigest
|
||||
|
||||
object CardanoMetadataWriter {
|
||||
|
||||
// Function to generate SHA-256 hash of a string
|
||||
def generateHash(transactionData: String): String = {
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
val hashBytes = digest.digest(transactionData.getBytes("UTF-8"))
|
||||
hashBytes.map("%02x".format(_)).mkString
|
||||
}
|
||||
|
||||
// Function to write metadata JSON file
|
||||
def writeMetadataFile(transactionHash: String, filePath: String): Unit = {
|
||||
val jsonContent =
|
||||
s"""
|
||||
|{
|
||||
| "674": {
|
||||
| "transaction_hash": "$transactionHash"
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin
|
||||
|
||||
val file = new File(filePath)
|
||||
val writer = new PrintWriter(file)
|
||||
writer.write(jsonContent)
|
||||
writer.close()
|
||||
println(s"Metadata file written to: $filePath")
|
||||
}
|
||||
|
||||
// Function to submit transaction to Cardano
|
||||
def submitHashToCardano(transactionHash: String, txIn: String, txOut: String, signingKey: String, network: String): Unit = {
|
||||
val metadataFilePath = "metadata.json"
|
||||
|
||||
// Write metadata to file
|
||||
writeMetadataFile(transactionHash, metadataFilePath)
|
||||
|
||||
// Build transaction
|
||||
val buildCommand = s"cardano-cli transaction build-raw --tx-in $txIn --tx-out $txOut --metadata-json-file $metadataFilePath --out-file tx.raw"
|
||||
buildCommand.!
|
||||
|
||||
// Sign transaction
|
||||
val signCommand = s"cardano-cli transaction sign --tx-body-file tx.raw --signing-key-file $signingKey --$network --out-file tx.signed"
|
||||
signCommand.!
|
||||
|
||||
// Submit transaction
|
||||
val submitCommand = s"cardano-cli transaction submit --tx-file tx.signed --$network"
|
||||
submitCommand.!
|
||||
|
||||
println("Transaction submitted to Cardano blockchain.")
|
||||
}
|
||||
|
||||
// Example Usage
|
||||
def main(args: Array[String]): Unit = {
|
||||
val transactionData = "123|100.50|EUR|2025-03-16 12:30:00"
|
||||
val transactionHash = generateHash(transactionData)
|
||||
|
||||
val txIn = "8c293647e5cb51c4d29e57e162a0bb4a0500096560ce6899a4b801f2b69f2813:0" // This is a tx_id:0 ///"YOUR_UTXO_HERE" // Replace with actual UTXO
|
||||
val txOut = "addr_test1qruvtthh7mndxu2ncykn47tksar9yqr3u97dlkq2h2dhzwnf3d755n99t92kp4rydpzgv7wmx4nx2j0zzz0g802qvadqtczjhn:1234" // "YOUR_RECEIVER_ADDRESS+LOVELACE" // Replace with receiver address and amount
|
||||
val signingKey = "payment.skey" // Path to your signing key file
|
||||
val network = "--testnet-magic" // "--testnet-magic 1097911063" // Use --mainnet for mainnet transactions
|
||||
|
||||
submitHashToCardano(transactionHash, txIn, txOut, signingKey, network)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Create second wallet
|
||||
// Find version of Pre Prod i'm running
|
||||
// Get CLI for that version
|
||||
// Use faucet to get funds
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
import com.bloxbean.cardano.client.account.Account
|
||||
import com.bloxbean.cardano.client.api.UtxoSupplier
|
||||
import com.bloxbean.cardano.client.backend.impl.local.LocalNodeBackendService
|
||||
import com.bloxbean.cardano.client.backend.api.TransactionService
|
||||
import com.bloxbean.cardano.client.backend.api.UtxoService
|
||||
import com.bloxbean.cardano.client.backend.model.Utxo
|
||||
import com.bloxbean.cardano.client.common.model.Network
|
||||
import com.bloxbean.cardano.client.metadata.cbor.CBORMetadata
|
||||
import com.bloxbean.cardano.client.transaction.spec.Transaction
|
||||
import com.bloxbean.cardano.client.api.helper.TransactionBuilder
|
||||
import java.security.MessageDigest
|
||||
|
||||
object CardanoMetadataWriter {
|
||||
|
||||
// Function to generate SHA-256 hash
|
||||
def generateHash(transactionData: String): String = {
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
val hashBytes = digest.digest(transactionData.getBytes("UTF-8"))
|
||||
hashBytes.map("%02x".format(_)).mkString
|
||||
}
|
||||
|
||||
// Function to submit metadata transaction
|
||||
def submitMetadataToCardano(mnemonic: String, transactionData: String): Unit = {
|
||||
val network = Network.TESTNET // Change to Network.MAINNET for mainnet
|
||||
|
||||
// Load Daedalus wallet from mnemonic
|
||||
val account = new Account(network, mnemonic)
|
||||
|
||||
// Generate hash of transaction data
|
||||
val transactionHash = generateHash(transactionData)
|
||||
|
||||
println(s"Generated Hash: $transactionHash")
|
||||
|
||||
// Create metadata object
|
||||
val metadata = new CBORMetadata()
|
||||
metadata.put("674", Map("transaction_hash" -> transactionHash))
|
||||
|
||||
// Initialize local Cardano node backend
|
||||
val backendService = new LocalNodeBackendService("http://localhost:8080")
|
||||
val transactionService: TransactionService = backendService.getTransactionService
|
||||
val utxoService: UtxoService = backendService.getUtxoService
|
||||
|
||||
// Get available UTXOs from the wallet
|
||||
val utxos: java.util.List[Utxo] = utxoService.getUtxos(account.baseAddress, 1, 10).getValue
|
||||
|
||||
if (utxos.isEmpty) {
|
||||
println("No UTXOs found. Please fund your wallet.")
|
||||
return
|
||||
}
|
||||
|
||||
// Build transaction
|
||||
val transaction = TransactionBuilder.create()
|
||||
.account(account)
|
||||
.metadata(metadata)
|
||||
.utxos(utxos)
|
||||
.changeAddress(account.baseAddress)
|
||||
.network(network)
|
||||
.build()
|
||||
|
||||
// Sign transaction
|
||||
val signedTransaction: Transaction = account.sign(transaction)
|
||||
|
||||
// Submit transaction
|
||||
val txHash: String = transactionService.submitTransaction(signedTransaction).getValue
|
||||
println(s"✅ Transaction submitted! TxHash: $txHash")
|
||||
}
|
||||
|
||||
// Main method
|
||||
def main(args: Array[String]): Unit = {
|
||||
val mnemonic = "YOUR_12_OR_24_WORD_MNEMONIC_HERE"
|
||||
val transactionData = "123|100.50|USD|2025-03-16 12:30:00"
|
||||
|
||||
submitMetadataToCardano(mnemonic, transactionData)
|
||||
}
|
||||
}
|
||||
*/
|
||||
@ -30,6 +30,7 @@ object MappedConsentProvider extends ConsentProvider {
|
||||
override def updateConsentStatus(consentId: String, status: ConsentStatus): Box[MappedConsent] = {
|
||||
MappedConsent.find(By(MappedConsent.mConsentId, consentId)) match {
|
||||
case Full(consent) =>
|
||||
Consent.expireAllPreviousValidBerlinGroupConsents(consent, status)
|
||||
tryo(consent
|
||||
.mStatus(status.toString)
|
||||
.mLastActionDate(now) //maybe not right, but for the create we use the `now`, we need to update it later.
|
||||
|
||||
@ -71,7 +71,9 @@ trait ConsumersProvider {
|
||||
developerEmail: Option[String],
|
||||
redirectURL: Option[String],
|
||||
createdByUserId: Option[String],
|
||||
certificate: Option[String] = None): Box[Consumer]
|
||||
certificate: Option[String] = None,
|
||||
logoUrl: Option[String] = None
|
||||
): Box[Consumer]
|
||||
def populateMissingUUIDs(): Boolean
|
||||
|
||||
}
|
||||
@ -389,7 +389,9 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
|
||||
developerEmail: Option[String],
|
||||
redirectURL: Option[String],
|
||||
createdByUserId: Option[String],
|
||||
certificate: Option[String]): Box[Consumer] = {
|
||||
certificate: Option[String],
|
||||
logoUrl: Option[String],
|
||||
): Box[Consumer] = {
|
||||
|
||||
val consumer: Box[Consumer] =
|
||||
// 1st try to find via UUID issued by OBP-API back end
|
||||
@ -473,6 +475,10 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
|
||||
case Some(v) => c.clientCertificate(v)
|
||||
case None =>
|
||||
}
|
||||
logoUrl match {
|
||||
case Some(v) => c.logoUrl(v)
|
||||
case None =>
|
||||
}
|
||||
consumerId match {
|
||||
case Some(v) => c.consumerId(v)
|
||||
case None =>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package code.scheduler
|
||||
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.util.APIUtil
|
||||
import code.consent.{ConsentStatus, MappedConsent}
|
||||
import code.util.Helper.MdcLoggable
|
||||
@ -52,7 +53,7 @@ object ConsentScheduler extends MdcLoggable {
|
||||
|
||||
val outdatedConsents = MappedConsent.findAll(
|
||||
By(MappedConsent.mStatus, ConsentStatus.received.toString),
|
||||
By(MappedConsent.mApiStandard, ApiVersion.berlinGroupV13.apiStandard),
|
||||
By(MappedConsent.mApiStandard, ConstantsBG.berlinGroupVersion1.apiStandard),
|
||||
By_<(MappedConsent.updatedAt, SchedulerUtil.someSecondsAgo(seconds))
|
||||
)
|
||||
|
||||
@ -78,7 +79,7 @@ object ConsentScheduler extends MdcLoggable {
|
||||
|
||||
val expiredConsents = MappedConsent.findAll(
|
||||
By(MappedConsent.mStatus, ConsentStatus.valid.toString),
|
||||
By(MappedConsent.mApiStandard, ApiVersion.berlinGroupV13.apiStandard),
|
||||
By(MappedConsent.mApiStandard, ConstantsBG.berlinGroupVersion1.apiStandard),
|
||||
By_<(MappedConsent.mValidUntil, new Date())
|
||||
)
|
||||
|
||||
|
||||
@ -110,7 +110,7 @@ class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510
|
||||
for {
|
||||
// Fetch the consent by ID
|
||||
consent: MappedConsent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
|
||||
APIUtil.unboxFullOrFail(_, None, s"$ConsentNotFound ($consentId)", 404)
|
||||
APIUtil.unboxFullOrFail(_, None, s"$ConsentNotFound ($consentId)", 400)
|
||||
}
|
||||
// Update the consent JWT with new access details
|
||||
consentJWT <- Consent.updateAccountAccessOfBerlinGroupConsentJWT(
|
||||
@ -171,6 +171,19 @@ class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510
|
||||
// Select all IBANs
|
||||
selectedAccountsIbansValue.set(userIbans)
|
||||
|
||||
var canReadAccountsIbansAvailableAccounts: List[String] = List()
|
||||
if(json.access.availableAccounts.contains("allAccounts")) { //
|
||||
/*
|
||||
Access is requested via:
|
||||
"access":
|
||||
{
|
||||
"availableAccounts": "allAccounts"
|
||||
}
|
||||
*/
|
||||
accessAccountsDefinedVar.set(true)
|
||||
canReadAccountsIbansAvailableAccounts = userIbans.toList
|
||||
}
|
||||
|
||||
// Determine which IBANs the user can access for accounts, balances, and transactions
|
||||
val canReadAccountsIbans: List[String] = json.access.accounts match {
|
||||
case Some(accounts) if accounts.isEmpty => // Access is requested via "accounts": []
|
||||
@ -226,7 +239,7 @@ class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510
|
||||
}
|
||||
|
||||
// all Selected IBANs
|
||||
val ibansFromGetConsentResponseJson = (canReadAccountsIbans ::: canReadBalancesIbans ::: canReadTransactionsIbans).distinct
|
||||
val ibansFromGetConsentResponseJson = (canReadAccountsIbansAvailableAccounts ::: canReadAccountsIbans ::: canReadBalancesIbans ::: canReadTransactionsIbans).distinct
|
||||
|
||||
/**
|
||||
* Generates toggle switches for IBAN lists.
|
||||
@ -385,10 +398,19 @@ class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510
|
||||
*/
|
||||
private def denyConsentRequestProcess() = {
|
||||
val consentId = ObpS.param("CONSENT_ID") openOr ("")
|
||||
Consents.consentProvider.vend.updateConsentStatus(consentId, ConsentStatus.rejected)
|
||||
S.redirectTo(
|
||||
s"$redirectUriValue?CONSENT_ID=${consentId}"
|
||||
)
|
||||
Consents.consentProvider.vend.getConsentByConsentId(consentId) match {
|
||||
case Full(consent) if otpValue.is == consent.challenge =>
|
||||
updateConsentUser(consent)
|
||||
updateConsentJwt(consent) map { i =>
|
||||
Consents.consentProvider.vend.updateConsentStatus(consentId, ConsentStatus.rejected)
|
||||
}
|
||||
S.redirectTo(
|
||||
s"$redirectUriValue?CONSENT_ID=${consentId}"
|
||||
)
|
||||
case _ =>
|
||||
S.error(ErrorMessages.ConsentNotFound)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -398,15 +420,29 @@ class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510
|
||||
val consentId = ObpS.param("CONSENT_ID") openOr ("")
|
||||
Consents.consentProvider.vend.getConsentByConsentId(consentId) match {
|
||||
case Full(consent) if otpValue.is == consent.challenge =>
|
||||
Consents.consentProvider.vend.updateConsentStatus(consentId, ConsentStatus.valid)
|
||||
updateConsentUser(consent)
|
||||
updateConsentJwt(consent) map { i =>
|
||||
Consents.consentProvider.vend.updateConsentStatus(consentId, ConsentStatus.valid)
|
||||
}
|
||||
S.redirectTo(
|
||||
s"/confirm-bg-consent-request-redirect-uri?CONSENT_ID=${consentId}"
|
||||
)
|
||||
case _ =>
|
||||
S.error("Wrong OTP value")
|
||||
S.error(ErrorMessages.OneTimePasswordInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
private def updateConsentUser(consent: MappedConsent): Box[MappedConsent] = {
|
||||
val loggedInUser = AuthUser.currentUser.flatMap(_.user.foreign).openOrThrowException(ErrorMessages.UserNotLoggedIn)
|
||||
Consents.consentProvider.vend.updateConsentUser(consent.consentId, loggedInUser)
|
||||
val jwt = Consent.updateUserIdOfBerlinGroupConsentJWT(loggedInUser.userId, consent, None).openOrThrowException(ErrorMessages.InvalidConnectorResponse)
|
||||
Consents.consentProvider.vend.setJsonWebToken(consent.consentId, jwt)
|
||||
}
|
||||
private def updateConsentJwt(consent: MappedConsent) = {
|
||||
val loggedInUser = AuthUser.currentUser.flatMap(_.user.foreign).openOrThrowException(ErrorMessages.UserNotLoggedIn)
|
||||
Consent.updateViewsOfBerlinGroupConsentJWT(loggedInUser, consent, None)
|
||||
}
|
||||
|
||||
private def getTppRedirectUri() = {
|
||||
val consentId = ObpS.param("CONSENT_ID") openOr ("")
|
||||
s"$redirectUriValue?CONSENT_ID=${consentId}"
|
||||
|
||||
@ -31,7 +31,9 @@ Berlin 13359, Germany
|
||||
<H1>Here are the debugging pages.</H1>
|
||||
|
||||
<h3><a href="/debug/debug-plain">debug-plain -- no Liftweb involved. </a></h3>
|
||||
<h3><a href="/debug/debug-basic">debug-basic -- call LiftWeb code 'surround'.</a></h3>
|
||||
<h3><a href="/debug/debug-basic">debug-basic (default)-- call LiftWeb default code 'surround'.</a></h3>
|
||||
<h3><a href="/debug/debug-default-header">debug-default-header -- call LiftWeb default header code 'surround'.</a></h3>
|
||||
<h3><a href="/debug/debug-default-footer">debug-default-footer -- call LiftWeb default footer code 'surround'.</a></h3>
|
||||
<h3><a href="/debug/debug-localization">debug-localization -- call Localization 'lift:loc' method.</a></h3>
|
||||
<h3><a href="/debug/debug-webui">debug-webui -- call webui method 'apiDocumentationLink' method.</a></h3>
|
||||
|
||||
|
||||
20
obp-api/src/main/webapp/debug/debug-default-footer.html
Normal file
20
obp-api/src/main/webapp/debug/debug-default-footer.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
|
||||
|
||||
|
||||
<head><title>Basic Liftweb Suround with default</title></head>
|
||||
|
||||
<div id="main" data-lift="surround?with=default-footer;at=content" tabindex="-1">
|
||||
|
||||
<h1>I call LiftWeb code surround</h1>
|
||||
with a <a href="http://www.example.com">link</a>
|
||||
<h1>Link to static</h1><a href="/static">static image</a>
|
||||
<h1>Link to SDKs</h1><a href="/sdks">SDKs</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
20
obp-api/src/main/webapp/debug/debug-default-header.html
Normal file
20
obp-api/src/main/webapp/debug/debug-default-header.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
|
||||
|
||||
|
||||
<head><title>Basic Liftweb Suround with default</title></head>
|
||||
|
||||
<div id="main" data-lift="surround?with=default-header;at=content" tabindex="-1">
|
||||
|
||||
<h1>I call LiftWeb code surround</h1>
|
||||
with a <a href="http://www.example.com">link</a>
|
||||
<h1>Link to static</h1><a href="/static">static image</a>
|
||||
<h1>Link to SDKs</h1><a href="/sdks">SDKs</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
275
obp-api/src/main/webapp/templates-hidden/default-footer.html
Normal file
275
obp-api/src/main/webapp/templates-hidden/default-footer.html
Normal file
@ -0,0 +1,275 @@
|
||||
<!--
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2017, TESOBE GmbH.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE GmbH.
|
||||
Osloerstrasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Sebastian Henschel: sebastian AT tesobe DOT com
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="description" content="Webapp of the Open Bank Project API">
|
||||
<meta name="keywords" content="OBP, Open Bank Project, API, bank, fintech">
|
||||
<meta name="author" content="TESOBE">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
|
||||
<title data-lift="WebUI.pageTitle">Open Bank Project: </title>
|
||||
<link href="/media/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
|
||||
<link id="favicon_link" rel="favicon icon" data-lift="WebUI.faviconLink" href="/favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="/media/css/select2.min.css">
|
||||
<link href="/media/css/toastr.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="/media/css/cookies-consent.css" rel="stylesheet">
|
||||
<link href="/media/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
|
||||
<link id="main_style_sheet" data-lift="WebUI.mainStyleSheet" href="/media/css/website.css?201707241207" rel="stylesheet" type="text/css" />
|
||||
<link id="override_style_sheet" data-lift="WebUI.overrideStyleSheet" href="" rel="stylesheet" type="text/css" />
|
||||
<link rel="stylesheet" href="/font-awesome/css/all.min.css">
|
||||
|
||||
<script src="/media/js/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="/media/js/bootstrap.min.js" type="text/javascript"></script>
|
||||
<script src="/media/js/select2.min.js"></script>
|
||||
<script src="/media/js/toastr.min.js" type="text/javascript"></script>
|
||||
<script src="/media/js/website.js" type="text/javascript"></script>
|
||||
<script src="/media/js/cookies-consent.js"></script>
|
||||
<script src="/media/js/moment-with-locales.min.js"></script>
|
||||
<script src="/media/js/bootstrap-datetimepicker.min.js"></script>
|
||||
<script src="/media/js/popper.min.js"></script>
|
||||
<script type="module" defer src="/media/js/inactivity.js"></script> <!-- The script loads “in the background”, and then runs when the DOM is fully built. -->
|
||||
</head>
|
||||
<body id="page_init">
|
||||
<div id="cookies-consent" data-lift="WebUI.cookieConsent">
|
||||
<div id="cookies-consent-font"> We use cookies to support session management.</div>
|
||||
<input id="cookies-consent-button" class="btn btn-default" type="button" value="Accept and close" onclick="removeByIdAndSaveIndicatorCookie('cookies-consent')"/>
|
||||
</div>
|
||||
|
||||
<div id="toast-container" class="toast-top-right" aria-live="assertive" role="alert" aria-atomic="true" aria-relevant="additions removals"></div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<!---
|
||||
|
||||
<header>
|
||||
<div id="header-decoration"></div>
|
||||
|
||||
<div id="cookie-ipaddress-concurrent-logins" data-lift="WebUI.concurrentLoginsCookiesCheck"></div>
|
||||
|
||||
<div id="top-text" data-lift="WebUI.topText"></div>
|
||||
<table id="table-header" aria-describedby="Home Page Logo">
|
||||
<tr class="row">
|
||||
<td>
|
||||
<th scope="col"></th>
|
||||
</td>
|
||||
<td id="td-logo-left-xs">
|
||||
<div data-lift="WebUI.headerLogoLeft">
|
||||
<a href="/" aria-label="Home Page"><img src="" id="logo-left-xs" align="left" alt="left logo image"></a>
|
||||
</div>
|
||||
</td>
|
||||
<td id="td-logo-right-xs">
|
||||
<div data-lift="WebUI.headerLogoRight">
|
||||
<a href="/" aria-label="Home Page"><img src="" id="logo-right-xs" align="right" alt="right logo image"></a>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="table-header-script" data-lift="WebUI.headerContentLoader"></div>
|
||||
<div id="messages-container" data-lift="Msgs"></div>
|
||||
</header>
|
||||
-->
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
<a class="sr-only sr-only-focusable" href ="#main" id="index-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#register-consumer" id="consumer-registration-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#api_documentation_content" id="introduction-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#authorise" id="logon-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#recover-password" id="lost-password-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#signup" id="sign-up-page">Skip to main content</a>
|
||||
|
||||
-->
|
||||
<!--
|
||||
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<div class="container">
|
||||
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="navitem" data-lift="WebUI.headerLogoLeft">
|
||||
<a id ="navitem-logo" class="navlink " href="/" ><img src="/media/images/logo.png" alt="homepage logo"></a>
|
||||
</li>
|
||||
<li class="navitem">
|
||||
<a class="navlink api-explorer-link" data-lift="WebUI.apiExplorerLink" href="">
|
||||
<lift:loc locid="api_explorer">API Explorer</lift:loc>
|
||||
</a>
|
||||
</li>
|
||||
<li data-lift="Nav.item?name=Consumer%20Registration&showEvenIfRestricted=true" class="navitem">
|
||||
<a id ="get-api-key-link" class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
|
||||
</li>
|
||||
<li class="navitem">
|
||||
<a class="navlink" id="sandbox-introduction-link" data-lift="WebUI.sandboxIntroductionLink" href="">
|
||||
<lift:loc locid="introduction">Introduction</lift:loc>
|
||||
</a>
|
||||
</li>
|
||||
<li class="navitem">
|
||||
<a class="navlink" id="technical-faqs-anchor" data-lift="WebUI.technicalFaqsAnchor" href="">
|
||||
<lift:loc locid="support">Support</lift:loc>
|
||||
</a>
|
||||
</li>
|
||||
<li class="navitem" data-lift="WebUI.subscriptionsButton" >
|
||||
<a class="navlink subscriptions-button" href="">
|
||||
<lift:loc locid="subscriptions"><div class ="subscriptions-button-text"></div></lift:loc>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
|
||||
<li data-lift="Nav.item?name=Login" class="navitem">
|
||||
|
||||
<p data-lift="WebUI.userIsLoggedIn">
|
||||
<a href="/user_mgt/sign_up" class="navbar-btn" id ="register-link"><lift:loc locid="register">Register</lift:loc></a><a data-lift="Login.loggedOut" href="#" class="btn btn-danger login"><lift:loc locid="logon">Log on</lift:loc></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li class="navitem" data-lift="Login.loggedIn" >
|
||||
|
||||
<p class="navbar-btn"><a href="/user-information"><span id="loggedIn-username">username</span></a><a href="#" class="btn btn-default logout">Log off <span id="countdown-timer-span" class="badge text-bg-primary"></span></a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
-->
|
||||
|
||||
<!--
|
||||
|
||||
<div id="small-screen-navbar">
|
||||
<button tabindex="0" id="small-nav-collapse" onclick="openNav()" aria-label ="Open Navigation" aria-hidden="false"></button>
|
||||
<div id="small-nav-logo" data-lift="WebUI.headerLogoLeft">
|
||||
<a href="/" aria-label="HomePage Logo"><img src="" class="logo-responsive-design-mobile" alt ="Home Page Logo"></a>
|
||||
</div>
|
||||
<div id="small-nav-log-on-button" data-lift="Nav.item?name=Login">
|
||||
<a data-lift="Login.loggedOut" href="#" class="btn btn-danger login">Log on</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="obp-sidebar" class="sidebar">
|
||||
<div id="small-screen-navbar">
|
||||
<button tabindex="0" id="small-nav-cross" onclick="closeNav()" aria-hidden="true" aria-label ="Close Navigation"></button>
|
||||
<div id="small-nav-logo" data-lift="WebUI.headerLogoLeft">
|
||||
<a href="/" aria-label="HomePage Logo"><img src="" class="logo-responsive-design-mobile" alt ="Home Page Logo"></a>
|
||||
</div>
|
||||
<div id="small-nav-log-on-button" data-lift="Nav.item?name=Login">
|
||||
<a data-lift="Login.loggedOut" href="#" class="btn btn-danger login">Log on</a>
|
||||
</div>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="navitem">
|
||||
<a class="navlink api-explorer-link" data-lift="WebUI.apiExplorerLink" href="">API Explorer</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="navitem" data-lift="Nav.item?name=Consumer%20Registration&showEvenIfRestricted=true" id="sideba-api-key-div">
|
||||
<a id ="get-api-key-link" class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="navitem">
|
||||
<a class="navlink" id="sandbox-introduction-link" data-lift="WebUI.sandboxIntroductionLink" href="">Introduction</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="navitem" id="technical-faqs-anchor-nav">
|
||||
<a class="navlink" id="technical-faqs-anchor" data-lift="WebUI.technicalFaqsAnchor" href="">
|
||||
<lift:loc locid="support">Support</lift:loc></a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="navitem" id="register-link-nav">
|
||||
<a data-lift="WebUI.userIsLoggedIn" href="/user_mgt/sign_up" class="navbar-btn" id ="register-link">Register</a>
|
||||
<a data-lift="Login.loggedIn" href="#" class="logout">Log off</a></p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
-->
|
||||
|
||||
<section id="content">
|
||||
<lift:bind name="content"/>
|
||||
The main content gets bound here
|
||||
</section>
|
||||
|
||||
|
||||
<footer>
|
||||
<div id="footer-div">
|
||||
<ul>
|
||||
<li>
|
||||
<a class="termsAndConditions-link" data-lift="WebUI.termsAndConditions" href="">
|
||||
<lift:loc locid="terms_conditions">Terms and Conditions</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="privacy-policy-link" data-lift="WebUI.privacyPolicyLink" href="https://openbankproject.com/privacy-policy">
|
||||
<lift:loc locid="privacy_policy">Privacy Policy</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="footer-div-social" href="http://twitter.com/#!/OpenBankProject" data-lift="WebUI.footerSocialLink">Twitter</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/OpenBankProject/OBP-API/">Github</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="api-documentation-link" data-lift="WebUI.apiDocumentationLink" href="">
|
||||
<lift:loc locid="api_documentation">API Documentation</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="sofi-link" data-lift="WebUI.sofiLink" href="">Sofit</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/user_mgt/sign_up?after-signup=link-to-customer" class="navbar-btn" id ="register-link">
|
||||
<lift:loc locid="register">On Board</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="api-link" data-lift="WebUI.apiLinkHuman" href="">
|
||||
<lift:loc locid="api_host">This API Host</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="commit-id-link" data-lift="WebUI.commitIdLink" href="">
|
||||
<lift:loc locid="api_host">GitHub commit</lift:loc></a>
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
<div class="language-tag" data-lift="WebUI.currentPage">
|
||||
<span><a href="#">Language</a></span>
|
||||
<div id="supported-language-list"></div>
|
||||
</div>
|
||||
<div id="copyright">
|
||||
<a href="http://openbankproject.com"><lift:loc locid="open_bank_project_is">Open Bank Project is ©2011 - </lift:loc> <span id="copyright-year" data-lift="WebUI.currentYearText">2018</span> </a> <a href="http://tesobe.com"><lift:loc locid="and_commercial_licenses">TESOBE and distributed under the AGPL and commercial licenses. </lift:loc></a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
264
obp-api/src/main/webapp/templates-hidden/default-header.html
Normal file
264
obp-api/src/main/webapp/templates-hidden/default-header.html
Normal file
@ -0,0 +1,264 @@
|
||||
<!--
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2017, TESOBE GmbH.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE GmbH.
|
||||
Osloerstrasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
by
|
||||
Simon Redfern : simon AT tesobe DOT com
|
||||
Sebastian Henschel: sebastian AT tesobe DOT com
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="description" content="Webapp of the Open Bank Project API">
|
||||
<meta name="keywords" content="OBP, Open Bank Project, API, bank, fintech">
|
||||
<meta name="author" content="TESOBE">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
|
||||
<title data-lift="WebUI.pageTitle">Open Bank Project: </title>
|
||||
<link href="/media/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
|
||||
<link id="favicon_link" rel="favicon icon" data-lift="WebUI.faviconLink" href="/favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="/media/css/select2.min.css">
|
||||
<link href="/media/css/toastr.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="/media/css/cookies-consent.css" rel="stylesheet">
|
||||
<link href="/media/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
|
||||
<link id="main_style_sheet" data-lift="WebUI.mainStyleSheet" href="/media/css/website.css?201707241207" rel="stylesheet" type="text/css" />
|
||||
<link id="override_style_sheet" data-lift="WebUI.overrideStyleSheet" href="" rel="stylesheet" type="text/css" />
|
||||
<link rel="stylesheet" href="/font-awesome/css/all.min.css">
|
||||
|
||||
<script src="/media/js/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="/media/js/bootstrap.min.js" type="text/javascript"></script>
|
||||
<script src="/media/js/select2.min.js"></script>
|
||||
<script src="/media/js/toastr.min.js" type="text/javascript"></script>
|
||||
<script src="/media/js/website.js" type="text/javascript"></script>
|
||||
<script src="/media/js/cookies-consent.js"></script>
|
||||
<script src="/media/js/moment-with-locales.min.js"></script>
|
||||
<script src="/media/js/bootstrap-datetimepicker.min.js"></script>
|
||||
<script src="/media/js/popper.min.js"></script>
|
||||
<script type="module" defer src="/media/js/inactivity.js"></script> <!-- The script loads “in the background”, and then runs when the DOM is fully built. -->
|
||||
</head>
|
||||
<body id="page_init">
|
||||
<div id="cookies-consent" data-lift="WebUI.cookieConsent">
|
||||
<div id="cookies-consent-font"> We use cookies to support session management.</div>
|
||||
<input id="cookies-consent-button" class="btn btn-default" type="button" value="Accept and close" onclick="removeByIdAndSaveIndicatorCookie('cookies-consent')"/>
|
||||
</div>
|
||||
|
||||
<div id="toast-container" class="toast-top-right" aria-live="assertive" role="alert" aria-atomic="true" aria-relevant="additions removals"></div>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<div id="header-decoration"></div>
|
||||
|
||||
<div id="cookie-ipaddress-concurrent-logins" data-lift="WebUI.concurrentLoginsCookiesCheck"></div>
|
||||
|
||||
<div id="top-text" data-lift="WebUI.topText"></div>
|
||||
<table id="table-header" aria-describedby="Home Page Logo">
|
||||
<tr class="row">
|
||||
<td>
|
||||
<th scope="col"></th>
|
||||
</td>
|
||||
<td id="td-logo-left-xs">
|
||||
<div data-lift="WebUI.headerLogoLeft">
|
||||
<a href="/" aria-label="Home Page"><img src="" id="logo-left-xs" align="left" alt="left logo image"></a>
|
||||
</div>
|
||||
</td>
|
||||
<td id="td-logo-right-xs">
|
||||
<div data-lift="WebUI.headerLogoRight">
|
||||
<a href="/" aria-label="Home Page"><img src="" id="logo-right-xs" align="right" alt="right logo image"></a>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="table-header-script" data-lift="WebUI.headerContentLoader"></div>
|
||||
<div id="messages-container" data-lift="Msgs"></div>
|
||||
</header>
|
||||
<a class="sr-only sr-only-focusable" href ="#main" id="index-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#register-consumer" id="consumer-registration-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#api_documentation_content" id="introduction-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#authorise" id="logon-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#recover-password" id="lost-password-page">Skip to main content</a>
|
||||
<a class="sr-only sr-only-focusable" href ="#signup" id="sign-up-page">Skip to main content</a>
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<div class="container">
|
||||
<!-- <div class="navbar-header">-->
|
||||
<!-- <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">-->
|
||||
<!-- <span class="sr-only">Toggle navigation</span>-->
|
||||
<!-- <span class="icon-bar"></span>-->
|
||||
<!-- <span class="icon-bar"></span>-->
|
||||
<!-- <span class="icon-bar"></span>-->
|
||||
<!-- </button>-->
|
||||
<!-- </div>-->
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="navitem" data-lift="WebUI.headerLogoLeft">
|
||||
<a id ="navitem-logo" class="navlink " href="/" ><img src="/media/images/logo.png" alt="homepage logo"></a>
|
||||
</li>
|
||||
<li class="navitem">
|
||||
<a class="navlink api-explorer-link" data-lift="WebUI.apiExplorerLink" href="">
|
||||
<lift:loc locid="api_explorer">API Explorer</lift:loc>
|
||||
</a>
|
||||
</li>
|
||||
<li data-lift="Nav.item?name=Consumer%20Registration&showEvenIfRestricted=true" class="navitem">
|
||||
<a id ="get-api-key-link" class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
|
||||
</li>
|
||||
<li class="navitem">
|
||||
<a class="navlink" id="sandbox-introduction-link" data-lift="WebUI.sandboxIntroductionLink" href="">
|
||||
<lift:loc locid="introduction">Introduction</lift:loc>
|
||||
</a>
|
||||
</li>
|
||||
<li class="navitem">
|
||||
<a class="navlink" id="technical-faqs-anchor" data-lift="WebUI.technicalFaqsAnchor" href="">
|
||||
<lift:loc locid="support">Support</lift:loc>
|
||||
</a>
|
||||
</li>
|
||||
<li class="navitem" data-lift="WebUI.subscriptionsButton" >
|
||||
<a class="navlink subscriptions-button" href="">
|
||||
<lift:loc locid="subscriptions"><div class ="subscriptions-button-text"></div></lift:loc>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<!-- login state -->
|
||||
<li data-lift="Nav.item?name=Login" class="navitem">
|
||||
<!-- LOGGED OUT -->
|
||||
<p data-lift="WebUI.userIsLoggedIn">
|
||||
<a href="/user_mgt/sign_up" class="navbar-btn" id ="register-link"><lift:loc locid="register">Register</lift:loc></a><a data-lift="Login.loggedOut" href="#" class="btn btn-danger login"><lift:loc locid="logon">Log on</lift:loc></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li class="navitem" data-lift="Login.loggedIn" >
|
||||
<!-- LOGGED IN -->
|
||||
<p class="navbar-btn"><a href="/user-information"><span id="loggedIn-username">username</span></a><a href="#" class="btn btn-default logout">Log off <span id="countdown-timer-span" class="badge text-bg-primary"></span></a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</nav>
|
||||
<div id="small-screen-navbar">
|
||||
<button tabindex="0" id="small-nav-collapse" onclick="openNav()" aria-label ="Open Navigation" aria-hidden="false"></button>
|
||||
<div id="small-nav-logo" data-lift="WebUI.headerLogoLeft">
|
||||
<a href="/" aria-label="HomePage Logo"><img src="" class="logo-responsive-design-mobile" alt ="Home Page Logo"></a>
|
||||
</div>
|
||||
<div id="small-nav-log-on-button" data-lift="Nav.item?name=Login">
|
||||
<a data-lift="Login.loggedOut" href="#" class="btn btn-danger login">Log on</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="obp-sidebar" class="sidebar">
|
||||
<div id="small-screen-navbar">
|
||||
<button tabindex="0" id="small-nav-cross" onclick="closeNav()" aria-hidden="true" aria-label ="Close Navigation"></button>
|
||||
<div id="small-nav-logo" data-lift="WebUI.headerLogoLeft">
|
||||
<a href="/" aria-label="HomePage Logo"><img src="" class="logo-responsive-design-mobile" alt ="Home Page Logo"></a>
|
||||
</div>
|
||||
<div id="small-nav-log-on-button" data-lift="Nav.item?name=Login">
|
||||
<a data-lift="Login.loggedOut" href="#" class="btn btn-danger login">Log on</a>
|
||||
</div>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="navitem">
|
||||
<a class="navlink api-explorer-link" data-lift="WebUI.apiExplorerLink" href="">API Explorer</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="navitem" data-lift="Nav.item?name=Consumer%20Registration&showEvenIfRestricted=true" id="sideba-api-key-div">
|
||||
<a id ="get-api-key-link" class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="navitem">
|
||||
<a class="navlink" id="sandbox-introduction-link" data-lift="WebUI.sandboxIntroductionLink" href="">Introduction</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="navitem" id="technical-faqs-anchor-nav">
|
||||
<a class="navlink" id="technical-faqs-anchor" data-lift="WebUI.technicalFaqsAnchor" href="">
|
||||
<lift:loc locid="support">Support</lift:loc></a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="navitem" id="register-link-nav">
|
||||
<a data-lift="WebUI.userIsLoggedIn" href="/user_mgt/sign_up" class="navbar-btn" id ="register-link">Register</a>
|
||||
<a data-lift="Login.loggedIn" href="#" class="logout">Log off</a></p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<section id="content">
|
||||
<lift:bind name="content"/>
|
||||
The main content gets bound here
|
||||
</section>
|
||||
|
||||
<!--
|
||||
<footer>
|
||||
<div id="footer-div">
|
||||
<ul>
|
||||
<li>
|
||||
<a class="termsAndConditions-link" data-lift="WebUI.termsAndConditions" href="">
|
||||
<lift:loc locid="terms_conditions">Terms and Conditions</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="privacy-policy-link" data-lift="WebUI.privacyPolicyLink" href="https://openbankproject.com/privacy-policy">
|
||||
<lift:loc locid="privacy_policy">Privacy Policy</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="footer-div-social" href="http://twitter.com/#!/OpenBankProject" data-lift="WebUI.footerSocialLink">Twitter</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/OpenBankProject/OBP-API/">Github</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="api-documentation-link" data-lift="WebUI.apiDocumentationLink" href="">
|
||||
<lift:loc locid="api_documentation">API Documentation</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="sofi-link" data-lift="WebUI.sofiLink" href="">Sofit</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/user_mgt/sign_up?after-signup=link-to-customer" class="navbar-btn" id ="register-link">
|
||||
<lift:loc locid="register">On Board</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="api-link" data-lift="WebUI.apiLinkHuman" href="">
|
||||
<lift:loc locid="api_host">This API Host</lift:loc></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="commit-id-link" data-lift="WebUI.commitIdLink" href="">
|
||||
<lift:loc locid="api_host">GitHub commit</lift:loc></a>
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
<div class="language-tag" data-lift="WebUI.currentPage">
|
||||
<span><a href="#">Language</a></span>
|
||||
<div id="supported-language-list"></div>
|
||||
</div>
|
||||
<div id="copyright">
|
||||
<a href="http://openbankproject.com"><lift:loc locid="open_bank_project_is">Open Bank Project is ©2011 - </lift:loc> <span id="copyright-year" data-lift="WebUI.currentYearText">2018</span> </a> <a href="http://tesobe.com"><lift:loc locid="and_commercial_licenses">TESOBE and distributed under the AGPL and commercial licenses. </lift:loc></a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
-->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,6 +1,7 @@
|
||||
package code.api.ResourceDocs1_4_0
|
||||
|
||||
import code.api.ResourceDocs1_4_0.ResourceDocs140.ImplementationsResourceDocs
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn}
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
|
||||
@ -283,7 +284,7 @@ class ResourceDocsTest extends ResourceDocsV140ServerSetup with PropsReset with
|
||||
}
|
||||
|
||||
scenario(s"We will test ${ApiEndpoint1.name} Api -v1.3", ApiEndpoint1, VersionOfApi) {
|
||||
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "v1.3" / "obp").GET
|
||||
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / ConstantsBG.berlinGroupVersion1.apiShortVersion / "obp").GET
|
||||
val responseGetObp = makeGetRequest(requestGetObp)
|
||||
And("We should get 200 and the response can be extract to case classes")
|
||||
val responseDocs = responseGetObp.body.extract[ResourceDocsJson]
|
||||
@ -293,7 +294,7 @@ class ResourceDocsTest extends ResourceDocsV140ServerSetup with PropsReset with
|
||||
}
|
||||
|
||||
scenario(s"We will test ${ApiEndpoint1.name} Api -BGv1.3", ApiEndpoint1, VersionOfApi) {
|
||||
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / "BGv1.3" / "obp").GET
|
||||
val requestGetObp = (ResourceDocsV4_0Request / "resource-docs" / s"BG${ConstantsBG.berlinGroupVersion1.apiShortVersion}" / "obp").GET
|
||||
val responseGetObp = makeGetRequest(requestGetObp)
|
||||
And("We should get 200 and the response can be extract to case classes")
|
||||
val responseDocs = responseGetObp.body.extract[ResourceDocsJson]
|
||||
@ -540,7 +541,7 @@ class ResourceDocsTest extends ResourceDocsV140ServerSetup with PropsReset with
|
||||
}
|
||||
|
||||
scenario(s"We will test ${ApiEndpoint2.name} Api -v1.3", ApiEndpoint1, VersionOfApi) {
|
||||
val requestGetObp = (ResourceDocsV1_4Request /"banks"/ testBankId1.value/ "resource-docs" / "v1.3" / "obp").GET
|
||||
val requestGetObp = (ResourceDocsV1_4Request /"banks"/ testBankId1.value/ "resource-docs" / ConstantsBG.berlinGroupVersion1.apiShortVersion / "obp").GET
|
||||
val responseGetObp = makeGetRequest(requestGetObp)
|
||||
And("We should get 200 and the response can be extract to case classes")
|
||||
val responseDocs = responseGetObp.body.extract[ResourceDocsJson]
|
||||
@ -550,7 +551,7 @@ class ResourceDocsTest extends ResourceDocsV140ServerSetup with PropsReset with
|
||||
}
|
||||
|
||||
scenario(s"We will test ${ApiEndpoint2.name} Api -BGv1.3", ApiEndpoint1, VersionOfApi) {
|
||||
val requestGetObp = (ResourceDocsV1_4Request /"banks"/ testBankId1.value/ "resource-docs" / "BGv1.3" / "obp").GET
|
||||
val requestGetObp = (ResourceDocsV1_4Request /"banks"/ testBankId1.value/ "resource-docs" / s"BG${ConstantsBG.berlinGroupVersion1.apiShortVersion}" / "obp").GET
|
||||
val responseGetObp = makeGetRequest(requestGetObp)
|
||||
And("We should get 200 and the response can be extract to case classes")
|
||||
val responseDocs = responseGetObp.body.extract[ResourceDocsJson]
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package code.api.berlin.group.v1_3
|
||||
|
||||
import code.api.Constant
|
||||
import code.api.Constant.{SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID}
|
||||
import code.api.Constant.{SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID}
|
||||
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3._
|
||||
import code.api.builder.AccountInformationServiceAISApi.APIMethods_AccountInformationServiceAISApi
|
||||
import code.api.util.APIUtil
|
||||
@ -9,10 +9,9 @@ import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.v4_0_0.PostViewJsonV400
|
||||
import code.consent.ConsentStatus
|
||||
import code.model.dataAccess.{BankAccountRouting, MappedBankAccount}
|
||||
import code.model.dataAccess.BankAccountRouting
|
||||
import code.setup.{APIResponse, DefaultUsers}
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.model.{AccountId, BankId, ErrorMessage}
|
||||
import com.openbankproject.commons.model.enums.AccountRoutingScheme
|
||||
import net.liftweb.json.Serialization.write
|
||||
import net.liftweb.mapper.By
|
||||
@ -103,13 +102,40 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit
|
||||
user1,
|
||||
PostViewJsonV400(view_id = SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID, is_system = true)
|
||||
)
|
||||
grantUserAccessToViewViaEndpoint(
|
||||
bankId,
|
||||
accountId,
|
||||
resourceUser1.userId,
|
||||
user1,
|
||||
PostViewJsonV400(view_id = SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID, is_system = true)
|
||||
)
|
||||
grantUserAccessToViewViaEndpoint(
|
||||
bankId,
|
||||
accountId,
|
||||
resourceUser1.userId,
|
||||
user1,
|
||||
PostViewJsonV400(view_id = SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID, is_system = true)
|
||||
)
|
||||
|
||||
val requestGet = (V1_3_BG / "accounts" / accountId).GET <@ (user1)
|
||||
val response = makeGetRequest(requestGet)
|
||||
|
||||
Then("We should get a 200 ")
|
||||
response.code should equal(200)
|
||||
response.body.extract[AccountDetailsJsonV13].account.resourceId should be (accountId)
|
||||
val jsonResponse = response.body.extract[AccountDetailsJsonV13]
|
||||
jsonResponse.account.resourceId should be (accountId)
|
||||
|
||||
jsonResponse.account._links.balances match {
|
||||
case Some(link) =>
|
||||
link.href.contains(berlinGroupVersion1) shouldBe true
|
||||
case None => // Nothing to check
|
||||
}
|
||||
jsonResponse.account._links.transactions match {
|
||||
case Some(link) =>
|
||||
link.href.contains(berlinGroupVersion1) shouldBe true
|
||||
case None => // Nothing to check
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,6 +258,68 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"BG v1.3 - $createConsent - postJsonBodyAvailableAccounts") {
|
||||
lazy val postJsonBody = PostConsentJson(
|
||||
access = ConsentAccessJson(
|
||||
accounts = None,
|
||||
balances = None,
|
||||
transactions = None,
|
||||
availableAccounts = Some("allAccounts"),
|
||||
allPsd2 = None
|
||||
),
|
||||
recurringIndicator = false,
|
||||
validUntil = getNextMonthDate(),
|
||||
frequencyPerDay = 1,
|
||||
combinedServiceIndicator = Some(false)
|
||||
)
|
||||
val postJsonBodyWrong1 = postJsonBody.copy(
|
||||
access = postJsonBody.access.copy(
|
||||
availableAccounts = Some("wrong")
|
||||
)
|
||||
)
|
||||
val postJsonBodyWrong2 = postJsonBody.copy(
|
||||
frequencyPerDay = 2
|
||||
)
|
||||
val postJsonBodyWrong3 = postJsonBody.copy(
|
||||
recurringIndicator = true
|
||||
)
|
||||
|
||||
scenario("Authentication User, test failed due to availableAccounts wrong value", BerlinGroupV1_3, createConsent) {
|
||||
val requestPost = (V1_3_BG / "consents" ).POST <@ (user1)
|
||||
val response: APIResponse = makePostRequest(requestPost, write(postJsonBodyWrong1))
|
||||
|
||||
Then("We should get a 400")
|
||||
response.code should equal(400)
|
||||
response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(BerlinGroupConsentAccessAvailableAccounts)
|
||||
}
|
||||
scenario("Authentication User, test failed due to frequency per day", BerlinGroupV1_3, createConsent) {
|
||||
val requestPost = (V1_3_BG / "consents" ).POST <@ (user1)
|
||||
val response: APIResponse = makePostRequest(requestPost, write(postJsonBodyWrong2))
|
||||
|
||||
Then("We should get a 400")
|
||||
response.code should equal(400)
|
||||
response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(BerlinGroupConsentAccessFrequencyPerDay)
|
||||
}
|
||||
scenario("Authentication User, test failed due to recurringIndicator = true", BerlinGroupV1_3, createConsent) {
|
||||
val requestPost = (V1_3_BG / "consents" ).POST <@ (user1)
|
||||
val response: APIResponse = makePostRequest(requestPost, write(postJsonBodyWrong3))
|
||||
|
||||
Then("We should get a 400")
|
||||
response.code should equal(400)
|
||||
response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(BerlinGroupConsentAccessRecurringIndicator)
|
||||
}
|
||||
scenario("Authentication User, test succeed", BerlinGroupV1_3, createConsent) {
|
||||
val requestPost = (V1_3_BG / "consents" ).POST <@ (user1)
|
||||
val response: APIResponse = makePostRequest(requestPost, write(postJsonBody))
|
||||
|
||||
Then("We should get a 201 ")
|
||||
response.code should equal(201)
|
||||
val jsonResponse = response.body.extract[PostConsentResponseJson]
|
||||
jsonResponse.consentId should not be (empty)
|
||||
jsonResponse.consentStatus should be (ConsentStatus.received.toString)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"BG v1.3 - $createConsent") {
|
||||
scenario("Authentication User, test succeed", BerlinGroupV1_3, createConsent) {
|
||||
val testBankId = testAccountId1
|
||||
@ -262,8 +350,9 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit
|
||||
|
||||
Then("We should get a 201 ")
|
||||
response.code should equal(201)
|
||||
response.body.extract[PostConsentResponseJson].consentId should not be (empty)
|
||||
response.body.extract[PostConsentResponseJson].consentStatus should be (ConsentStatus.received.toString)
|
||||
val jsonResponse = response.body.extract[PostConsentResponseJson]
|
||||
jsonResponse.consentId should not be (empty)
|
||||
jsonResponse.consentStatus should be (ConsentStatus.received.toString)
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,7 +573,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit
|
||||
scenario("Authentication User, only mocked data, just test succeed", BerlinGroupV1_3, updateConsentsPsuDataTransactionAuthorisation) {
|
||||
val requestStartConsentAuthorisation = (V1_3_BG / "consents"/"consentId" /"authorisations"/ "AUTHORISATIONID" ).PUT <@ (user1)
|
||||
val responseStartConsentAuthorisation = makePutRequest(requestStartConsentAuthorisation, """{"scaAuthenticationData":""}""")
|
||||
responseStartConsentAuthorisation.code should be (400)
|
||||
responseStartConsentAuthorisation.code should be (403)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -2,20 +2,21 @@ package code.api.berlin.group.v1_3
|
||||
|
||||
import code.api.Constant
|
||||
import code.api.Constant.SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.APIUtil.OAuth.{Consumer, Token}
|
||||
import code.api.v3_0_0.ViewJsonV300
|
||||
import code.api.v4_0_0.{PostAccountAccessJsonV400, PostViewJsonV400}
|
||||
import code.setup.ServerSetupWithTestData
|
||||
import code.views.Views
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import dispatch.Req
|
||||
import net.liftweb.json.Serialization.write
|
||||
import org.scalatest.Tag
|
||||
|
||||
trait BerlinGroupServerSetupV1_3 extends ServerSetupWithTestData {
|
||||
|
||||
val berlinGroupVersion1: String = ConstantsBG.berlinGroupVersion1.apiShortVersion
|
||||
object BerlinGroupV1_3 extends Tag("BerlinGroup_v1_3")
|
||||
val V1_3_BG = baseRequest / ApiVersion.berlinGroupV13.urlPrefix / "v1.3"
|
||||
val V1_3_BG = baseRequest / ConstantsBG.berlinGroupVersion1.urlPrefix / ConstantsBG.berlinGroupVersion1.apiShortVersion
|
||||
def v4_0_0_Request: Req = baseRequest / "obp" / "v4.0.0"
|
||||
|
||||
override def beforeEach() = {
|
||||
|
||||
@ -30,6 +30,7 @@ package code.util
|
||||
import code.api.Constant.SYSTEM_OWNER_VIEW_ID
|
||||
import code.api.UKOpenBanking.v2_0_0.{APIMethods_UKOpenBanking_200, OBP_UKOpenBanking_200}
|
||||
import code.api.UKOpenBanking.v3_1_0.{APIMethods_AccountAccessApi, OBP_UKOpenBanking_310}
|
||||
import code.api.berlin.group.ConstantsBG
|
||||
import code.api.berlin.group.v1_3.OBP_BERLIN_GROUP_1_3
|
||||
import code.api.builder.AccountInformationServiceAISApi.APIMethods_AccountInformationServiceAISApi
|
||||
import code.api.util.APIUtil.OBPEndpoint
|
||||
@ -42,6 +43,8 @@ import code.views.system.ViewDefinition
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
|
||||
class APIUtilHeavyTest extends V400ServerSetup with PropsReset {
|
||||
|
||||
val bgVersion = ConstantsBG.berlinGroupVersion1.apiShortVersion
|
||||
|
||||
feature("test APIUtil.versionIsAllowed method") {
|
||||
//This mean, we are only disabled the v4.0.0, all other versions should be enabled
|
||||
@ -101,70 +104,72 @@ class APIUtilHeavyTest extends V400ServerSetup with PropsReset {
|
||||
|
||||
|
||||
feature("test APIUtil.getAllowedEndpoints method") {
|
||||
val obpEndpointsV400: List[OBPEndpoint] = OBPAPI4_0_0.endpointsOf4_0_0.toList
|
||||
val obpAllResourceDocsV400 = Implementations4_0_0.resourceDocs
|
||||
scenario(s"Test the APIUtil.getAllowedEndpoints method") {
|
||||
val obpEndpointsV400: List[OBPEndpoint] = OBPAPI4_0_0.endpointsOf4_0_0.toList
|
||||
val obpAllResourceDocsV400 = Implementations4_0_0.resourceDocs
|
||||
|
||||
val allowedEndpoints: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(obpEndpointsV400, obpAllResourceDocsV400).toList
|
||||
val allowedEndpoints: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(obpEndpointsV400, obpAllResourceDocsV400).toList
|
||||
|
||||
val allowedOperationIds = allowedEndpoints.map(_.operationId)
|
||||
val allowedOperationIds = allowedEndpoints.map(_.operationId)
|
||||
|
||||
allowedOperationIds contains("OBPv4.0.0-getLogoutLink") should be (true)
|
||||
allowedOperationIds contains("OBPv4.0.0-getLogoutLink") should be (true)
|
||||
|
||||
|
||||
setPropsValues(
|
||||
"api_disabled_endpoints" -> "[OBPv4.0.0-getLogoutLink,OBPv4.0.0-getMapperDatabaseInfo,OBPv4.0.0-callsLimit,OBPv4.0.0-getBanks,OBPv4.0.0-ibanChecker]",
|
||||
"api_enabled_endpoints" -> "[]"
|
||||
)
|
||||
val allowedEndpoints2: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(obpEndpointsV400, obpAllResourceDocsV400).toList
|
||||
setPropsValues(
|
||||
"api_disabled_endpoints" -> "[OBPv4.0.0-getLogoutLink,OBPv4.0.0-getMapperDatabaseInfo,OBPv4.0.0-callsLimit,OBPv4.0.0-getBanks,OBPv4.0.0-ibanChecker]",
|
||||
"api_enabled_endpoints" -> "[]"
|
||||
)
|
||||
val allowedEndpoints2: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(obpEndpointsV400, obpAllResourceDocsV400).toList
|
||||
|
||||
val allowedOperationIds2 = allowedEndpoints2.map(_.operationId)
|
||||
val allowedOperationIds2 = allowedEndpoints2.map(_.operationId)
|
||||
|
||||
allowedOperationIds2 contains("OBPv4.0.0-getLogoutLink") should be (false)
|
||||
allowedOperationIds2 contains("OBPv4.0.0-getMapperDatabaseInfo") should be (false)
|
||||
allowedOperationIds2 contains("OBPv4.0.0-callsLimit") should be (false)
|
||||
allowedOperationIds2 contains("OBPv4.0.0-getLogoutLink") should be (false)
|
||||
allowedOperationIds2 contains("OBPv4.0.0-getMapperDatabaseInfo") should be (false)
|
||||
allowedOperationIds2 contains("OBPv4.0.0-callsLimit") should be (false)
|
||||
|
||||
|
||||
val bgResourceDocsV13 = APIMethods_AccountInformationServiceAISApi.resourceDocs
|
||||
val bgEndpointsV13 = APIMethods_AccountInformationServiceAISApi.endpoints
|
||||
val bgResourceDocsV13 = APIMethods_AccountInformationServiceAISApi.resourceDocs
|
||||
val bgEndpointsV13 = APIMethods_AccountInformationServiceAISApi.endpoints
|
||||
|
||||
setPropsValues(
|
||||
"api_disabled_endpoints" -> "[BGv1.3-createConsent,BGv1.3-deleteConsent]",
|
||||
"api_enabled_endpoints" -> "[]"
|
||||
)
|
||||
|
||||
val allowedEndpoints3: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(bgEndpointsV13, bgResourceDocsV13).toList
|
||||
val allowedOperationIds3 = allowedEndpoints3.map(_.operationId)
|
||||
|
||||
allowedOperationIds3 contains("BGv1.3-getCardAccountTransactionList") should be (true)
|
||||
allowedOperationIds3 contains("BGv1.3-createConsent") should be (false)
|
||||
allowedOperationIds3 contains("BGv1.3-deleteConsent") should be (false)
|
||||
setPropsValues(
|
||||
"api_disabled_endpoints" -> s"[BG${bgVersion}-createConsent,BG${bgVersion}-deleteConsent]",
|
||||
"api_enabled_endpoints" -> "[]"
|
||||
)
|
||||
|
||||
val ukResourceDocsV31 = APIMethods_AccountAccessApi.resourceDocs
|
||||
val ukEndpointsV31 = APIMethods_AccountAccessApi.endpoints
|
||||
val allowedEndpoints3: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(bgEndpointsV13, bgResourceDocsV13).toList
|
||||
val allowedOperationIds3 = allowedEndpoints3.map(_.operationId)
|
||||
|
||||
setPropsValues(
|
||||
"api_disabled_endpoints" -> "[UKv3.1-createAccountAccessConsents,UKv3.1-deleteConsent]",
|
||||
"api_enabled_endpoints" -> "[]"
|
||||
)
|
||||
allowedOperationIds3 contains(s"BG${bgVersion}-getCardAccountTransactionList") should be (true)
|
||||
allowedOperationIds3 contains(s"BG${bgVersion}-createConsent") should be (false)
|
||||
allowedOperationIds3 contains(s"BG${bgVersion}-deleteConsent") should be (false)
|
||||
|
||||
val allowedEndpoints4: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(ukEndpointsV31, ukResourceDocsV31).toList
|
||||
val allowedOperationIds4 = allowedEndpoints4.map(_.operationId)
|
||||
val ukResourceDocsV31 = APIMethods_AccountAccessApi.resourceDocs
|
||||
val ukEndpointsV31 = APIMethods_AccountAccessApi.endpoints
|
||||
|
||||
allowedOperationIds4 contains("UKv3.1-getAccountAccessConsentsConsentId") should be (true)
|
||||
allowedOperationIds4 contains("UKv3.1-createAccountAccessConsents") should be (false)
|
||||
allowedOperationIds4 contains("UKv3.1-deleteConsent") should be (false)
|
||||
setPropsValues(
|
||||
"api_disabled_endpoints" -> "[UKv3.1-createAccountAccessConsents,UKv3.1-deleteConsent]",
|
||||
"api_enabled_endpoints" -> "[]"
|
||||
)
|
||||
|
||||
setPropsValues(
|
||||
"api_disabled_endpoints" -> "[]",
|
||||
"api_enabled_endpoints" -> "[UKv3.1-createAccountAccessConsents]"
|
||||
)
|
||||
val allowedEndpoints4: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(ukEndpointsV31, ukResourceDocsV31).toList
|
||||
val allowedOperationIds4 = allowedEndpoints4.map(_.operationId)
|
||||
|
||||
val allowedEndpoints5: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(ukEndpointsV31, ukResourceDocsV31).toList
|
||||
val allowedOperationIds5 = allowedEndpoints5.map(_.operationId)
|
||||
allowedOperationIds4 contains("UKv3.1-getAccountAccessConsentsConsentId") should be (true)
|
||||
allowedOperationIds4 contains("UKv3.1-createAccountAccessConsents") should be (false)
|
||||
allowedOperationIds4 contains("UKv3.1-deleteConsent") should be (false)
|
||||
|
||||
allowedOperationIds5.length should be (1)
|
||||
allowedOperationIds5 contains("UKv3.1-createAccountAccessConsents") should be (true)
|
||||
allowedOperationIds5 contains("UKv3.1-deleteConsent") should be (false)
|
||||
setPropsValues(
|
||||
"api_disabled_endpoints" -> "[]",
|
||||
"api_enabled_endpoints" -> "[UKv3.1-createAccountAccessConsents]"
|
||||
)
|
||||
|
||||
val allowedEndpoints5: List[APIUtil.ResourceDoc] = APIUtil.getAllowedResourceDocs(ukEndpointsV31, ukResourceDocsV31).toList
|
||||
val allowedOperationIds5 = allowedEndpoints5.map(_.operationId)
|
||||
|
||||
allowedOperationIds5.length should be (1)
|
||||
allowedOperationIds5 contains("UKv3.1-createAccountAccessConsents") should be (true)
|
||||
allowedOperationIds5 contains("UKv3.1-deleteConsent") should be (false)
|
||||
}
|
||||
}
|
||||
|
||||
feature("test APIUtil.getPermissionPairFromViewDefinition method") {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user