Merge remote-tracking branch 'origin/develop' into TOBI-develop

This commit is contained in:
tawoe 2019-01-22 11:43:19 +01:00
commit d07002fd81
242 changed files with 6088 additions and 2432 deletions

View File

@ -19,6 +19,59 @@ Please comment your code ! :-) Imagine an engineer is trying to fix a production
When naming variables use strict camel case e.g. use myUrl not myURL. This is so we can automatically convert from camelCase to snake_case for JSON output.
## Writing an API endpoint
```scala
resourceDocs += ResourceDoc(
getCustomersForUser,
implementedInApiVersion,
nameOf(getCustomersForUser),
"GET",
"/users/current/customers",
"Get Customers for Current User",
s"""Gets all Customers that are linked to a User.
|
|
|${authenticationRequiredMessage(true)}
|
|""",
emptyObjectJson,
customerJsonV300,
List(
UserNotLoggedIn,
UserCustomerLinksNotFoundForUser,
UnknownError
),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagCustomer, apiTagUser, apiTagNewStyle))
// This can be considered a reference new style endpoint.
// This is a partial function. The lazy value should have a meaningful name.
lazy val getCustomersForUser : OBPEndpoint = {
// This defines the URL path and method (GET) for which this partial function will accept the call.
case "users" :: "current" :: "customers" :: Nil JsonGet _ => {
// We have the Call Context (cc) object (provided through the OBPEndpoint type)
// The Call Context contains the authorisation headers etc.
cc => {
for {
// Extract the user from the headers and get an updated callContext
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
// Now here is the business logic.
// Get The customers related to a user. Process the resonse which might be an Exception
(customers,callContext) <- Connector.connector.vend.getCustomersByUserIdFuture(u.userId, callContext) map {
unboxFullOrFail(_, callContext, ConnectorEmptyResponse, 400)
}
} yield {
// Create the JSON to return. We also return the callContext
(JSONFactory300.createCustomersJson(customers), HttpCode.`200`(callContext))
}
}
}
}
```
## Writing tests
When you write a test for an endpoint please tag it with a version and the endpoint.
@ -55,6 +108,33 @@ class FundsAvailableTest extends V310ServerSetup {
}
```
## Code Generation
We support to generate the OBP-API code from the following three types of json. You can choose one of them as your own requirements.
1 Choose one of the following types: type1 or type2 or type3
2 Modify the json file your selected,
3 Run the Main method according to your json file
4 Run/Restart OBP-API project.
5 Run API_Exploer project to test your new APIs. (click the Tag `APIBuilder B1)
Here are the three types:
Type1: If you use `modelSource.json`, please run `APIBuilderModel.scala` main method
```
OBP-API/src/main/scala/code/api/APIBuilder/APIModelSource.json
OBP-API/src/main/scala/code/api/APIBuilder/APIBuilderModel.scala
```
Type2: If you use `apisResource.json`, please run `APIBuilder.scala` main method
```
OBP-API/src/main/scala/code/api/APIBuilder/apiResourceDoc/apisResource.json
OBP-API/src/main/scala/code/api/APIBuilder/apiResourceDoc/APIBuilder.scala
```
Type3: If you use `swaggerResource.json`, please run `APIBuilderSwagger.scala` main method
```
OBP-API/src/main/scala/code/api/APIBuilder/swagger/swaggerResource.json
OBP-API/src/main/scala/code/api/APIBuilder/swagger/APIBuilderSwagger.scala
```
## Issues
If would like to report an issue or suggest any kind of improvement please use Github Issues.

View File

@ -452,26 +452,7 @@ You can obfuscate passwords in the props file the same way as for jetty:
* db.url=OBF:fdsafdsakwaetcetcetc
## Code Generation
We support to generate the OBP-API code from the following two types of json. You can choose one of them as your own requirements.
1 Choose one of the following types: type1 or type2
2 Modify the json file your selected,
3 Run the Main method according to your json file
4 Run/Restart OBP-API project.
5 Run API_Exploer project to test your new APIs. (click the Tag `APIBuilder B1)
Here are the two types:
Type1: If you use `modelSource.json`, please run `APIBuilderModel.scala` main method
```
OBP-API/src/main/scala/code/api/APIBuilder/modelSource.json
OBP-API/src/main/scala/code/api/APIBuilder/APIBuilderModel.scala
```
Type2: If you use `apisResource.json`, please run `APIBuilder.scala` main method
```
OBP-API/src/main/scala/code/api/APIBuilder/apisResource.json
OBP-API/src/main/scala/code/api/APIBuilder/APIBuilder.scala
```
Please refer to the [Code Generation](https://github.com/OpenBankProject/OBP-API/blob/develop/CONTRIBUTING.md##code-generation) for links
## Using jetty password obfuscation with props file
@ -531,6 +512,39 @@ Info about rate limiting availibility at some instance can be found over next AP
}
}
```
## Webhooks
Webhooks are used to call external URLs when certain events happen.
Account Webhooks focus on events around accounts.
For instance, a webhook could be used to notify an external service if a balance changes on an account.
This functionality is work in progress!
There are 3 API's endpoint related to webhooks:
1. `POST ../banks/BANK_ID/account-web-hooks` - Create an Account Webhook
2. `PUT ../banks/BANK_ID/account-web-hooks` - Enable/Disable an Account Webhook
3. `GET ../management/banks/BANK_ID/account-web-hooks` - Get Account Webhooks
---
## OAuth 2.0
In order to enable an OAuth2 workflow at an instance of OBP-API backend app you need to setup next props:
```
# -- OAuth 2 ---------------------------------------------------------------
# Enable/Disable OAuth 2 workflow at a server instance
# In case isn't defined default value is false
# allow_oauth2_login=false
# URL of Public server JWK set used for validating bearer JWT access tokens
# oauth2.jwk_set.url=http://localhost:8080/jwk.json
# ----------------------------------------------------------- OAuth 2 ------
OpenID Connect is supported.
Tested Identity providers: Google, MITREId.
```
### Example for Google's OAuth 2.0 implementation for authentication, which conforms to the OpenID Connect specification
```
allow_oauth2_login=true
oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs
```
---
## Scala / Lift

37
cheat_sheet.md Normal file
View File

@ -0,0 +1,37 @@
#OBP Cheat Sheet.
## A selection of links to get you started using the Open Bank Project platform and surrounding tools.
## Note! *An alternative version of this document is available at the glossary* [here](https://apiexplorersandbox.openbankproject.com/glossary#OBP.Cheat-Sheet)
[OBP API Installation](https://github.com/OpenBankProject/OBP-API/blob/develop/README.md)
[OBP API Contributing](https://github.com/OpenBankProject/OBP-API/blob/develop/CONTRIBUTING.md)
[Access Control](https://apiexplorersandbox.openbankproject.com/glossary#API.Access-Control)
[Authentication](https://github.com/OpenBankProject/OBP-API/wiki/Authentication)
[Interfaces](https://apiexplorersandbox.openbankproject.com/glossary#API.Interfaces)
[Endpoints](https://apiexplorersandbox.openbankproject.com)
[Versioning](https://github.com/OpenBankProject/OBP-API/wiki/API-Versioning)
[Glossary](https://apiexplorersandbox.openbankproject.com/glossary)
[Access Control](https://apiexplorersandbox.openbankproject.com/glossary#API.Access-Control)
[OBP Kafka](https://apiexplorersandbox.openbankproject.com/glossary#Adapter.Kafka.Intro)
[OBP Akka](https://apiexplorersandbox.openbankproject.com/glossary#Adapter.Akka.Intro)
[API Explorer](https://github.com/OpenBankProject/API-Explorer/blob/develop/README.md)
[API Manager](https://github.com/OpenBankProject/API-Manager/blob/master/README.md)
[API Tester](https://github.com/OpenBankProject/API-Tester/blob/master/README.md)

10
credits.md Normal file
View File

@ -0,0 +1,10 @@
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
Marko Milić: marko AT tesobe DOT com
Hongwei Zhang: hongwei AT tesobe DOT com
Tobias Woelk: tobias.woelk AT tesobe DOT com

26
pom.xml
View File

@ -13,7 +13,7 @@
<properties>
<scala.version>2.12</scala.version>
<scala.compiler>2.12.4</scala.compiler>
<akka.version>2.5.13</akka.version>
<akka.version>2.5.19</akka.version>
<akka-streams-kafka.version>0.22</akka-streams-kafka.version>
<kafka.version>1.1.0</kafka.version>
<avro.version>1.8.3</avro.version>
@ -91,11 +91,6 @@
<artifactId>dispatch-lift-json_${scala.version}</artifactId>
<version>0.13.1</version>
</dependency>
<dependency>
<groupId>net.databinder.dispatch</groupId>
<artifactId>dispatch-core_${scala.version}</artifactId>
<version>0.13.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
@ -220,11 +215,6 @@
<artifactId>pegdown</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>com.jason-goodwin</groupId>
<artifactId>authentikat-jwt_${scala.version}</artifactId>
<version>0.4.5</version>
</dependency>
<dependency>
<groupId>com.tokbox</groupId>
<artifactId>opentok-server-sdk</artifactId>
@ -293,6 +283,12 @@
<artifactId>obp-ri-transport</artifactId>
<version>${obp-ri.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.typesafe.akka/akka-http-core -->
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-http-core_${scala.version}</artifactId>
<version>10.1.6</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_${scala.version}</artifactId>
@ -352,7 +348,7 @@
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>4.23</version>
<version>6.0</version>
</dependency>
<dependency>
<groupId>com.github.OpenBankProject</groupId>
@ -371,6 +367,12 @@
<version>0.1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.nimbusds/oauth2-oidc-sdk -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
<version>5.17.1</version>
</dependency>
</dependencies>

View File

@ -11,6 +11,7 @@ connector=mapped
#connector=mongodb
#connector=kafka
#conenctor=kafka_vMar2017
#connector=akka_vDec2018
#connector=obpjvm
#connector=...
@ -238,6 +239,19 @@ BankMockKey=change_me
## Web interface configuration
## IMPLEMENTING BANK SPECIFIC BRANDING ON ONE OBP INSTANCE ########################
# Note, you can specify bank specific branding by appending _FOR_BRAND_<BANK_ID> to the standard props names
# e.g.
#webui_header_logo_left_url_FOR_BRAND_banku = https://static.openbankproject.com/images/sandbox/bank_u.png
#webui_header_logo_left_url_FOR_BRAND_banky = https://static.openbankproject.com/images/sandbox/bank_y.png
# And invoke by calling index etc with ?brand=<BANK_ID>
# e.g. http://127.0.0.1:8080/?brand=x
# For any properties that don't have a bank specific flavour, the standard props name will be used.
####################################################################################
webui_header_logo_left_url = /media/images/logo.png
webui_header_logo_right_url =
webui_index_page_about_section_background_image_url = /media/images/about-background.jpg
@ -335,7 +349,7 @@ webui_agree_terms_url =
# If we want to gather more informaiton about an Applicaiton / Startup fill this url and text
# If we want to gather more information about an Application / Startup fill this url and text
# Will be displayed on the post Consumer Registration page.
#webui_post_consumer_registration_more_info_url =
#webui_post_consumer_registration_more_info_text = Please tell us more your Application and / or Startup using this link.
@ -393,7 +407,7 @@ super_admin_user_ids=USER_ID1,USER_ID2,
##########################
## OpenId Connect can be used to retrieve User informaiton from an
## OpenId Connect can be used to retrieve User information from an
## external OpenID Connect server.
## To use an external OpenID Connect server,
## you will need to change these values.
@ -463,18 +477,13 @@ display_internal_errors=false
# -------------------------------------- Display internal errors --
# -- OAuth 2 --------------------------------------------------------------------
# -- OAuth 2 ---------------------------------------------------------------
# Enable/Disable OAuth 2 workflow at a server instance
# In case isn't defined default value is false
# allow_oauth2_login=false
# oauth2.token_secret=secret
# Enable SSL for JWT
# If set to true must set paths for the keystore locations
# In case isn't defined default value is false
# oauth2.jwt.use.ssl=false
# URL of Public server JWK set used for validating bearer JWT access tokens
# oauth2.jwk_set.url=http://localhost:8080/jwk.json
# ----------------------------------------------------------- OAuth 2 -----------
# ----------------------------------------------------------- OAuth 2 ------
## This property is used for documenting at Resource Doc. It may include the port also (but not /obp)
## (this needs to be a URL)
@ -522,4 +531,28 @@ featured_apis=elasticSearchWarehouseV300
# Define mapper rules
# In case isn't defined default value is "false"
# mapper_rules.create_foreign_keys=false
# -----------------------------------------------
# -----------------------------------------------
# -- Akka connector --------------------------------------------
# Define mapper rules
# In case isn't defined default value is "127.0.0.1"
# akka_connector.hostname=127.0.0.1
# In case isn't defined default value is "2662"
# akka_connector.port=2662
# In case isn't defined default value is "INFO"
# akka_connector.loglevel=INFO/DEBUG etc.
# In case isn't defined default value is "akka-connector-actor"
# akka_connector.name_of_actor=SOME_ACTOR_NAME
# --------------------------------------------------------------
# -- Scopes ---------------------------------------------
# Scopes are a way to limit the APIs a Consumer can call.
# In case isn't defined default value is "false"
# require_scopes=false
# -------------------------------------------------------
# -- Database scheduler -----------------------------
# Database scheduler interval in seconds.
# Scheduler would not be started if delay is not set.
database_messages_scheduler_interval=3600
# ---------------------------------------------------

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,24 +16,20 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package bootstrap.liftweb
import java.io.{File, FileInputStream}
import java.util.{Locale, TimeZone}
import code.accountapplication.MappedAccountApplication
import code.accountholder.MapperAccountHolders
import code.actorsystem.ObpActorSystem
import code.api.Constant._
@ -42,7 +38,7 @@ import code.api.ResourceDocs1_4_0._
import code.api._
import code.api.builder.APIBuilder_Connector
import code.api.sandbox.SandboxApiCalls
import code.api.util.APIUtil.enableVersionIfAllowed
import code.api.util.APIUtil.{enableVersionIfAllowed, errorJsonResponse}
import code.api.util.{APIUtil, ApiVersion, ErrorMessages, Migration}
import code.atms.MappedAtm
import code.bankconnectors.Connector
@ -57,7 +53,7 @@ import code.customeraddress.MappedCustomerAddress
import code.entitlement.MappedEntitlement
import code.entitlementrequest.MappedEntitlementRequest
import code.fx.{MappedCurrency, MappedFXRate}
import code.kafka.{KafkaConsumer, KafkaHelperActors}
import code.kafka.{KafkaHelperActors, OBPKafkaConsumer}
import code.kycchecks.MappedKycCheck
import code.kycdocuments.MappedKycDocument
import code.kycmedias.MappedKycMedia
@ -77,6 +73,7 @@ import code.model.dataAccess._
import code.productAttributeattribute.MappedProductAttribute
import code.products.MappedProduct
import code.remotedata.RemotedataActors
import code.scheduler.DatabaseDriverScheduler
import code.scope.{MappedScope, MappedUserScope}
import code.snippet.{OAuthAuthorisation, OAuthWorkedThanks}
import code.socialmedia.MappedSocialMedia
@ -93,6 +90,7 @@ import javax.mail.internet.MimeMessage
import net.liftweb.common._
import net.liftweb.db.DBLogEntry
import net.liftweb.http._
import net.liftweb.json.Extraction
import net.liftweb.mapper._
import net.liftweb.sitemap.Loc._
import net.liftweb.sitemap._
@ -233,6 +231,12 @@ class Boot extends MdcLoggable {
logger.debug(s"If you can read this, logging level is debug")
val actorSystem = ObpActorSystem.startLocalActorSystem()
connector match {
case "akka_vDec2018" =>
// Start Actor system of Akka connector
ObpActorSystem.startNorthSideAkkaConnectorActorSystem()
case _ => // Do nothing
}
// where to search snippets
LiftRules.addToPackages("code")
@ -326,12 +330,12 @@ class Boot extends MdcLoggable {
logger.info(s"KafkaHelperActors.startLocalKafkaHelperWorkers( ${actorSystem} ) starting")
KafkaHelperActors.startLocalKafkaHelperWorkers(actorSystem)
// Start North Side Consumer if it's not already started
KafkaConsumer.primaryConsumer.start()
OBPKafkaConsumer.primaryConsumer.start()
}
if (!APIUtil.getPropsAsBoolValue("remotedata.enable", false)) {
if (APIUtil.getPropsAsBoolValue("use_akka", false) == true) {
try {
logger.info(s"RemotedataActors.startLocalRemotedataWorkers( ${actorSystem} ) starting")
logger.info(s"RemotedataActors.startActors( ${actorSystem} ) starting")
RemotedataActors.startActors(actorSystem)
} catch {
case ex: Exception => logger.warn(s"RemotedataActors.startLocalRemotedataWorkers( ${actorSystem} ) could not start: $ex")
@ -430,25 +434,41 @@ class Boot extends MdcLoggable {
logger.info("Would have sent email if not in dev mode: " + m.getContent)
})
implicit val formats = net.liftweb.json.DefaultFormats
LiftRules.exceptionHandler.prepend{
//same as default LiftRules.exceptionHandler
case(Props.RunModes.Development, r, e) => {
logger.error("Exception being returned to browser when processing " + r.uri.toString, e)
XhtmlResponse((<html> <body>Exception occured while processing {r.uri}<pre>{showException(e)}</pre> </body> </html>), S.htmlProperties.docType, List("Content-Type" -> "text/html; charset=utf-8"), Nil, 500, S.legacyIeCompatibilityMode)
JsonResponse(
Extraction.decompose(ErrorMessage(code = 500, message = s"${ErrorMessages.InternalServerError} ${showExceptionAtJson(e)}")),
500
)
}
//same as default LiftRules.exceptionHandler, except that it also send an email notification
case (_, r , e) => {
sendExceptionEmail(e)
logger.error("Exception being returned to browser when processing " + r.uri.toString, e)
XhtmlResponse((<html> <body>Something unexpected happened while serving the page at {r.uri}</body> </html>), S.htmlProperties.docType, List("Content-Type" -> "text/html; charset=utf-8"), Nil, 500, S.legacyIeCompatibilityMode)
JsonResponse(
Extraction.decompose(ErrorMessage(code = 500, message = s"${ErrorMessages.InternalServerError}")),
500
)
}
}
LiftRules.uriNotFound.prepend{
case (r, _) => NotFoundAsResponse(errorJsonResponse(
s"${ErrorMessages.InvalidUri}Current Url is (${r.uri.toString}), Current Content-Type Header is (${r.headers.find(_._1.equals("Content-Type")).map(_._2).getOrElse("")})",
404)
)
}
if ( !APIUtil.getPropsAsLongValue("transaction_status_scheduler_delay").isEmpty ) {
val delay = APIUtil.getPropsAsLongValue("transaction_status_scheduler_delay").openOrThrowException("Incorrect value for transaction_status_scheduler_delay, please provide number of seconds.")
TransactionStatusScheduler.start(delay)
}
APIUtil.getPropsAsLongValue("database_messages_scheduler_interval") match {
case Full(i) => DatabaseDriverScheduler.start(i)
case _ => // Do not start it
}
APIUtil.akkaSanityCheck() match {
case Full(c) if c == true => logger.info(s"remotedata.secret matched = $c")
@ -460,24 +480,26 @@ class Boot extends MdcLoggable {
case _ => throw new Exception(s"Unexpected error occurs during Akka sanity check!")
}
Migration.database.generateAndPopulateMissingCustomerUUIDs()
Migration.database.generateAndPopulateMissingConsumersUUIDs()
}
def schemifyAll() = {
Schemifier.schemify(true, Schemifier.infoF _, ToSchemify.models: _*)
if (APIUtil.getPropsAsBoolValue("remotedata.enable", false) == false) {
Schemifier.schemify(true, Schemifier.infoF _, ToSchemify.modelsRemotedata: _*)
}
}
private def showException(le: Throwable): String = {
val ret = "Message: " + le.toString + "\n\t" +
le.getStackTrace.map(_.toString).mkString("\n\t") + "\n"
private def showExceptionAtJson(error: Throwable): String = {
val formattedError = "Message: " + error.toString + error.getStackTrace.map(_.toString).mkString(" ")
val also = le.getCause match {
val formattedCause = error.getCause match {
case null => ""
case sub: Throwable => "\nCaught and thrown by:\n" + showException(sub)
case cause: Throwable => "Caught and thrown by: " + showExceptionAtJson(cause)
}
ret + also
formattedError + formattedCause
}
private def sendExceptionEmail(exception: Throwable): Unit = {
@ -548,7 +570,8 @@ object ToSchemify {
MappedUserScope,
MappedTaxResidence,
MappedCustomerAddress,
MappedUserAuthContext
MappedUserAuthContext,
MappedAccountApplication
)
// The following tables are accessed directly via Mapper / JDBC

View File

@ -0,0 +1,52 @@
package code.accountapplication
import java.util.Date
import code.api.util.APIUtil
import code.products.Products.ProductCode
import code.remotedata.RemotedataAccountApplication
import net.liftweb.common.Box
import net.liftweb.util.SimpleInjector
import scala.concurrent.Future
object AccountApplication extends SimpleInjector {
val accountApplication = new Inject(buildOne _) {}
def buildOne: AccountApplicationProvider =
APIUtil.getPropsAsBoolValue("use_akka", false) match {
case false => MappedAccountApplicationProvider
case true => RemotedataAccountApplication
}
}
trait AccountApplicationProvider {
def getAll(): Future[Box[List[AccountApplication]]]
def getById(accountApplicationId: String): Future[Box[AccountApplication]]
def createAccountApplication(productCode: ProductCode, userId: Option[String], customerId: Option[String]): Future[Box[AccountApplication]]
def updateStatus(accountApplicationId:String, status: String): Future[Box[AccountApplication]]
}
trait AccountApplication {
def accountApplicationId: String
def productCode: ProductCode
def userId: String
def customerId: String
def dateOfApplication: Date
def status: String
}
class RemotedataAccountApplicationCaseClasses {
case class getAll()
case class getById(accountApplicationId: String)
case class createAccountApplication(productCode: ProductCode, userId: Option[String], customerId: Option[String])
case class updateStatus(accountApplicationId:String, status: String)
}
object RemotedataAccountApplicationCaseClasses extends RemotedataAccountApplicationCaseClasses

View File

@ -0,0 +1,70 @@
package code.accountapplication
import java.util.Date
import code.api.util.ErrorMessages
import code.customer.{Customer, MappedCustomer}
import code.model.dataAccess.ResourceUser
import code.products.Products.ProductCode
import code.util.MappedUUID
import net.liftweb.common.{Box, Empty, Failure, Full}
import net.liftweb.mapper._
import net.liftweb.util.Helpers.tryo
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object MappedAccountApplicationProvider extends AccountApplicationProvider {
override def getAll(): Future[Box[List[AccountApplication]]] = Future {
tryo{MappedAccountApplication.findAll()}
}
override def getById(accountApplicationId: String): Future[Box[AccountApplication]] = Future {
MappedAccountApplication.find(By(MappedAccountApplication.mAccountApplicationId, accountApplicationId))
}
override def createAccountApplication(productCode: ProductCode, userId: Option[String], customerId: Option[String]): Future[Box[AccountApplication]] =
Future {
tryo {
MappedAccountApplication.create.mCode(productCode.value).mUserId(userId.orNull).mCustomerId(customerId.orNull).mStatus("REQUESTED").saveMe()
}
}
override def updateStatus(accountApplicationId:String, status: String): Future[Box[AccountApplication]] =
Future{
MappedAccountApplication.find(By(MappedAccountApplication.mAccountApplicationId, accountApplicationId))
match {
case Full(accountApplication) if(accountApplication.status == "ACCEPTED") =>
Failure(s"${ErrorMessages.AccountApplicationAlreadyAccepted} Current Account-Application-Id($accountApplicationId)")
case Full(accountApplication) => tryo{accountApplication.mStatus(status).saveMe()}
case Empty => Failure(s"${ErrorMessages.AccountApplicationNotFound} Current Account-Application-Id($accountApplicationId)")
case _ => Failure(ErrorMessages.UnknownError)
}
}
}
class MappedAccountApplication extends AccountApplication with LongKeyedMapper[MappedAccountApplication] with IdPK with CreatedUpdated {
def getSingleton = MappedAccountApplication
object mAccountApplicationId extends MappedUUID(this)
object mCode extends MappedString(this, 50)
object mCustomerId extends MappedUUID(this)
object mUserId extends MappedUUID(this) //resourceUser
object mStatus extends MappedString(this, 255)
override def accountApplicationId: String = mAccountApplicationId.get
override def productCode: ProductCode = ProductCode(mCode.get)
override def userId: String = mUserId.get
override def customerId: String = mCustomerId.get
override def dateOfApplication: Date = createdAt.get
override def status: String = mStatus.get
}
object MappedAccountApplication extends MappedAccountApplication with LongKeyedMetaMapper[MappedAccountApplication] {
override def dbIndexes = UniqueIndex(mAccountApplicationId) :: super.dbIndexes
}

View File

@ -16,8 +16,8 @@ trait ObpActorHelper {
}
case af: APIFailure => af
case f: Failure => f
case l: List[T] => l
case s: Set[T] => s
case l: List[_] => l.asInstanceOf[List[T]]
case s: Set[_] => s.asInstanceOf[Set[T]]
case Full(r) => r
case j: JValue => j
case t: T => t

View File

@ -1,13 +1,9 @@
package code.actorsystem
import akka.util.Timeout
import code.api.APIFailure
import code.api.util.APIUtil
import code.util.Helper.MdcLoggable
import net.liftweb.common._
import net.liftweb.util.Props
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
@ -20,29 +16,9 @@ trait ObpActorInit extends MdcLoggable{
logger.debug(s"Create this Actor: $actorName: ${actor}")
val TIMEOUT = (ACTOR_TIMEOUT seconds)
implicit val timeout = Timeout(ACTOR_TIMEOUT * (1000 milliseconds))
def extractFuture[T](f: Future[Any]): T = {
val r = f.map {
case s: Set[T] => s
case l: List[T] => l
case t: T => t
case _ => Empty ~> APIFailure(s"future extraction failed", 501)
}
Await.result(r, TIMEOUT).asInstanceOf[T]
}
def extractFutureToBox[T](f: Future[Any]): Box[T] = {
val r = f.map {
case pf: ParamFailure[_] => Empty ~> pf
case af: APIFailure => Empty ~> af
case f: Failure => f
case Empty => Empty
case t: T => Full(t)
case _ => Empty ~> APIFailure(s"future extraction to box failed", 501)
}
Await.result(r, TIMEOUT)
def getValueFromFuture[T](f: Future[T]): T = {
Await.result(f, TIMEOUT)
}
def CreateActorNameFromClassName(c: String): String = {

View File

@ -1,6 +1,7 @@
package code.actorsystem
import akka.actor.ActorSystem
import code.bankconnectors.akka.actor.AkkaConnectorActorConfig
import code.util.Helper
import code.util.Helper.MdcLoggable
import com.typesafe.config.ConfigFactory
@ -10,11 +11,21 @@ object ObpActorSystem extends MdcLoggable {
val props_hostname = Helper.getHostname
var obpActorSystem: ActorSystem = _
var northSideAkkaConnectorActorSystem: ActorSystem = _
def startLocalActorSystem(): ActorSystem = {
logger.info("Starting local actor system")
logger.info(ObpActorConfig.localConf)
obpActorSystem = ActorSystem.create(s"ObpActorSystem_${props_hostname}", ConfigFactory.load(ConfigFactory.parseString(ObpActorConfig.localConf)))
val localConf = ObpActorConfig.localConf
logger.info(localConf)
obpActorSystem = ActorSystem.create(s"ObpActorSystem_${props_hostname}", ConfigFactory.load(ConfigFactory.parseString(localConf)))
obpActorSystem
}
def startNorthSideAkkaConnectorActorSystem(): ActorSystem = {
logger.info("Starting North Side Akka Connector actor system")
val localConf = AkkaConnectorActorConfig.localConf
logger.info(localConf)
northSideAkkaConnectorActorSystem = ActorSystem.create(s"SouthSideAkkaConnector_${props_hostname}", ConfigFactory.load(ConfigFactory.parseString(localConf)))
northSideAkkaConnectorActorSystem
}
}

View File

@ -1,12 +1,13 @@
package code.actorsystem
import akka.actor.ActorSystem
import akka.actor.{ActorSelection, ActorSystem}
import code.api.util.APIUtil
import code.bankconnectors.akka.actor.{AkkaConnectorActorConfig, AkkaConnectorHelperActor}
import code.util.Helper
import code.util.Helper.MdcLoggable
import code.webhook.WebhookHelperActors
import com.typesafe.config.ConfigFactory
import net.liftweb.util.Props
import net.liftweb.common.Full
object ObpLookupSystem extends ObpLookupSystem {
@ -76,19 +77,53 @@ trait ObpLookupSystem extends MdcLoggable {
this.obpLookupSystem.actorSelection(actorPath)
}
def getWebhookActor() = {
/**
* This function is a Single Point Of Entry for Webhook's Actor
* I.e. we can obtain te Actor all over the code in next way:
* {
* val actor: ActorSelection = ObpLookupSystem.getWebhookActor()
* }
*
* @return An ActorSelection which is a logical view of a section of an ActorSystem's tree of Actors,
* allowing for broadcasting of messages to that section.
*/
def getWebhookActor(): ActorSelection = {
val name = WebhookHelperActors.actorName
val actorPath: String = {
val hostname = ObpActorConfig.localHostname
val port = ObpActorConfig.localPort
val props_hostname = Helper.getHostname
if (port == 0) {
logger.error("Failed to connect to local Web Hook actor")
logger.error("Failed to connect to local Webhook's actor")
}
s"akka.tcp://ObpActorSystem_${props_hostname}@${hostname}:${port}/user/${name}"
}
this.obpLookupSystem.actorSelection(actorPath)
}
def getAkkaConnectorActor(actorName: String) = {
val hostname = APIUtil.getPropsValue("akka_connector.hostname")
val port = APIUtil.getPropsValue("akka_connector.port")
val actorPath: String = (hostname, port) match {
case (Full(h), Full(p)) =>
val hostname = h
val port = p
val akka_connector_hostname = Helper.getAkkaConnectorHostname
s"akka.tcp://SouthSideAkkaConnector_${akka_connector_hostname}@${hostname}:${port}/user/${actorName}"
case _ =>
val hostname = AkkaConnectorActorConfig.localHostname
val port = AkkaConnectorActorConfig.localPort
val props_hostname = Helper.getHostname
if (port == 0) {
logger.error("Failed to find an available port.")
}
AkkaConnectorHelperActor.startAkkaConnectorHelperActors(ObpActorSystem.northSideAkkaConnectorActorSystem)
s"akka.tcp://SouthSideAkkaConnector_${props_hostname}@${hostname}:${port}/user/${actorName}"
}
this.obpLookupSystem.actorSelection(actorPath)
}
}

View File

@ -265,7 +265,7 @@ object APIBuilderModel
println("Congratulations! You make the new APIs. Please restart OBP-API server!")
}
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/modelSource.json")
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/APIModelSource.json")
//"/templates"
val apiUrl= getApiUrl(jsonJValueFromFile)

View File

@ -23,9 +23,10 @@ Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.APIBuilder;
package code.api.APIBuilder.apiResourceDoc;
import code.api.APIBuilder.APIBuilderModel._
import code.api.APIBuilder.APIBuilderModel
import code.api.util.APIUtil
import scala.meta._
import net.liftweb.json.JsonAST.{JObject, JString}
@ -35,7 +36,7 @@ object APIBuilder
{
def main(args: Array[String]): Unit = overwriteApiCode(apiSource,jsonFactorySource)
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/apisResource.json")
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/apiResourceDoc/apisResource.json")
val resourceDocsJObject= jsonJValueFromFile.\("resource_docs").children.asInstanceOf[List[JObject]]
@ -505,4 +506,4 @@ object JsonFactory_APIBuilder{
}
}
"""
}
}

View File

@ -0,0 +1,457 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
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 Ltd
Osloerstrasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.APIBuilder.swagger
import code.api.APIBuilder.APIBuilderModel._
import code.api.APIBuilder.APIBuilderModel
import code.api.util.APIUtil
import net.liftweb.json.JsonAST.{JObject, JString}
import net.liftweb.json.{JArray, JValue}
import scala.meta._
object APIBuilderSwagger
{
def main(args: Array[String]): Unit = overwriteApiCode(apiSource,jsonFactorySource)
val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/swagger/swaggerResource.json")
val getSingleApiResponseBody: JValue = jsonJValueFromFile \\("foo")\"foo"\"value"
//"template"
val modelName = getModelName(getSingleApiResponseBody)
//All the fields in the template object.
val modelFieldsJValue: JValue = (getSingleApiResponseBody \modelName).children.head
//TEMPLATE
val modelNameUpperCase = modelName.toUpperCase
//template
val modelNameLowerCase = modelName.toLowerCase
//Template
val modelNameCapitalized = modelNameLowerCase.capitalize
//MappedTemplate_123123
val modelMappedName = s"Mapped${modelNameCapitalized}_"+Math.abs(scala.util.Random.nextLong())
val modelTypeName = Type.Name(modelMappedName)
val modelTermName = Term.Name(modelMappedName)
val modelInit =Init.apply(Type.Name(modelMappedName), Term.Name(modelMappedName), Nil)
val getMultipleApiSummary: String = ((jsonJValueFromFile \\"get"\\ "summary")\("summary")).asInstanceOf[JArray].children(0).asInstanceOf[JString].values
val getSingleApiSummary: String = ((jsonJValueFromFile \\"get"\\ "summary")\("summary")).asInstanceOf[JArray].children(1).asInstanceOf[JString].values
val deleteSingleApiSummary: String = ((jsonJValueFromFile \\"delete"\\ "summary")\("summary")).asInstanceOf[JString].values
val createSingleApiSummary: String = ((jsonJValueFromFile \\"post"\\ "summary")\("summary")).asInstanceOf[JString].values
val getApiDescription: String = ((jsonJValueFromFile \\"get"\\ "description").obj.head.value).asInstanceOf[JString].values
val getSingleDescription: String = ((jsonJValueFromFile \\"get"\\ "description").obj(3).value).asInstanceOf[JString].values
val createApiDescription: String = ((jsonJValueFromFile \\"post"\\ "description").obj.head.value).asInstanceOf[JString].values
val deleteApiDescription: String = ((jsonJValueFromFile \\"delete"\\ "description").obj.head.value).asInstanceOf[JString].values
val getMultipleApiAuthenticationStatement: Term.ApplyInfix = getAuthenticationStatement(true)
val getSingleApiAuthenticationStatement: Term.ApplyInfix = getAuthenticationStatement(true)
val deleteSingleApiAuthenticationStatement: Term.ApplyInfix = getAuthenticationStatement(true)
val createSingleApiAuthenticationStatement: Term.ApplyInfix = getAuthenticationStatement(true)
val getMultipleApiUrl: String = (jsonJValueFromFile \\("paths")\"paths").asInstanceOf[JObject].obj(0).name
val getSingleApiUrl: String = (jsonJValueFromFile \\("paths")\"paths").asInstanceOf[JObject].obj(1).name
val getMultipleApiUrlVal = Lit.String(s"$getMultipleApiUrl")
val createSingleApiUrlVal = getMultipleApiUrl
val getApiUrlLiftFormat = getMultipleApiUrl.replaceFirst("/", "").split("/").mkString("""""",""" :: ""","""""")
val getApiUrlLiftweb: Lit.String = Lit.String(getApiUrlLiftFormat)
val createApiUrlLiftweb: Lit.String = Lit.String(getApiUrlLiftFormat)
val getSingleApiUrlVal = Lit.String(s"$getSingleApiUrl")
val deleteSingleApiUrlVal = Lit.String(s"$getSingleApiUrl")
val getSingleApiUrlLiftFormat = getSingleApiUrl.replaceFirst("/", "").split("/").dropRight(1).mkString("""""",""" :: ""","""""")
val getSingleApiUrlLiftweb: Lit.String = Lit.String(getSingleApiUrlLiftFormat)
val deleteApiUrlLiftweb: Lit.String = Lit.String(getSingleApiUrlLiftFormat)
val getMultipleApiSummaryVal = Lit.String(s"$getMultipleApiSummary")
val getSingleApiSummaryVal = Lit.String(s"$getSingleApiSummary")
val deleteSingleApiSummaryVal = Lit.String(s"$deleteSingleApiSummary")
val createSingleApiSummaryVal = Lit.String(s"$createSingleApiSummary")
val getMultipleApiDescriptionVal = Lit.String(s"$getApiDescription")
val createSingleApiDescriptionVal = Lit.String(s"$createApiDescription")
val getSingleApiDescriptionVal = Lit.String(s"$getSingleDescription")
val deleteSingleApiDescriptionVal = Lit.String(s"$deleteApiDescription")
val errorMessageBody: Lit.String = Lit.String(s"OBP-31001: ${modelNameCapitalized} not found. Please specify a valid value for ${modelNameUpperCase}_ID.")
val errorMessageName: Pat.Var = Pat.Var(Term.Name(s"${modelNameCapitalized}NotFound"))
val errorMessageVal: Defn.Val = q"""val TemplateNotFound = $errorMessageBody""".copy(pats = List(errorMessageName))
val errorMessage: Term.Name = Term.Name(errorMessageVal.pats.head.toString())
val getTemplatesResourceCode: Term.ApplyInfix =q"""
resourceDocs += ResourceDoc(
getTemplates,
apiVersion,
"getTemplates",
"GET",
$getMultipleApiUrlVal,
$getMultipleApiSummaryVal,
$getMultipleApiDescriptionVal,
emptyObjectJson,
templatesJson,
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
apiTagApiBuilder :: Nil
)"""
val getTemplatesPartialFunction: Defn.Val = q"""
lazy val getTemplates: OBPEndpoint ={
case ($getApiUrlLiftweb:: Nil) JsonGet req =>
cc =>
{
for{
u <- $getMultipleApiAuthenticationStatement
templates <- APIBuilder_Connector.getTemplates
templatesJson = JsonFactory_APIBuilder.createTemplates(templates)
jsonObject:JValue = decompose(templatesJson)
}yield{
successJsonResponse(jsonObject)
}
}
}"""
val createTemplateResourceCode: Term.ApplyInfix =q"""
resourceDocs += ResourceDoc(
createTemplate,
apiVersion,
"createTemplate",
"POST",
$createSingleApiUrlVal,
$createSingleApiSummaryVal,
$createSingleApiDescriptionVal,
createTemplateJson,
templateJson,
List(UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
apiTagApiBuilder :: Nil
)"""
val createTemplatePartialFunction: Defn.Val = q"""
lazy val createTemplate: OBPEndpoint ={
case ($createApiUrlLiftweb:: Nil) JsonPost json -> _ => {
cc =>
{
for{
createTemplateJson <- tryo(json.extract[CreateTemplateJson]) ?~! InvalidJsonFormat
u <- $createSingleApiAuthenticationStatement
template <- APIBuilder_Connector.createTemplate(createTemplateJson)
templateJson = JsonFactory_APIBuilder.createTemplate(template)
jsonObject:JValue = decompose(templateJson)
}yield{
successJsonResponse(jsonObject)
}
}
}
}
"""
val getTemplateResourceCode: Term.ApplyInfix =q"""
resourceDocs += ResourceDoc(
getTemplate,
apiVersion,
"getTemplate",
"GET",
$getSingleApiUrlVal,
$getSingleApiSummaryVal,
$getSingleApiDescriptionVal,
emptyObjectJson,
templateJson,
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
apiTagApiBuilder :: Nil
)"""
val getTemplatePartialFunction: Defn.Val = q"""
lazy val getTemplate: OBPEndpoint ={
case ($getSingleApiUrlLiftweb :: templateId :: Nil) JsonGet _ => {
cc =>
{
for{
u <- $getSingleApiAuthenticationStatement
template <- APIBuilder_Connector.getTemplateById(templateId) ?~! $errorMessage
templateJson = JsonFactory_APIBuilder.createTemplate(template)
jsonObject:JValue = decompose(templateJson)
}yield{
successJsonResponse(jsonObject)
}
}
}
}"""
val deleteTemplateResourceCode: Term.ApplyInfix = q"""
resourceDocs += ResourceDoc(
deleteTemplate,
apiVersion,
"deleteTemplate",
"DELETE",
$deleteSingleApiUrlVal,
$deleteSingleApiSummaryVal,
$deleteSingleApiDescriptionVal,
emptyObjectJson,
emptyObjectJson.copy("true"),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
apiTagApiBuilder :: Nil
)"""
val deleteTemplatePartialFunction: Defn.Val = q"""
lazy val deleteTemplate: OBPEndpoint ={
case ($deleteApiUrlLiftweb :: templateId :: Nil) JsonDelete _ => {
cc =>
{
for{
u <- $deleteSingleApiAuthenticationStatement
template <- APIBuilder_Connector.getTemplateById(templateId) ?~! $errorMessage
deleted <- APIBuilder_Connector.deleteTemplate(templateId)
}yield{
if(deleted)
noContentJsonResponse
else
errorJsonResponse("Delete not completed")
}
}
}
}
"""
//List(author, pages, points)
val modelFieldsNames: List[String] = getModelFieldsNames(modelFieldsJValue)
//List(String, Int, Double)
val modelFieldsTypes: List[String] = getModelFieldsTypes(modelFieldsNames, modelFieldsJValue)
//List(Chinua Achebe, 209, 1.3)
val modelFieldsDefaultValues: List[Any] = getModelFieldDefaultValues(modelFieldsNames, modelFieldsJValue)
//List(author: String = `Chinua Achebe`, tutor: String = `1123123 1312`, pages: Int = 209, points: Double = 1.3)
val modelCaseClassParams: List[Term.Param] = getModelCaseClassParams(modelFieldsNames, modelFieldsTypes, modelFieldsDefaultValues)
//def createTemplate(createTemplateJson: CreateTemplateJson) = Full(
// MappedTemplate_6099750036365020434.create
// .mTemplateId(UUID.randomUUID().toString)
// .mAuthor(createTemplateJson.author)
// .mPages(createTemplateJson.pages)
// .mPoints(createTemplateJson.points)
// .saveMe())
val createModelJsonMethod: Defn.Def = generateCreateModelJsonMethod(modelFieldsNames, modelMappedName)
//trait Template { `_` =>
// def author: String
// def tutor: String
// def pages: Int
// def points: Double
// def templateId: String
//}
val modelTrait: Defn.Trait = getModelTrait(modelFieldsNames, modelFieldsTypes)
//class MappedTemplate extends Template with LongKeyedMapper[MappedTemplate] with IdPK {
// object mAuthor extends MappedString(this, 100)
// override def author: String = mAuthor.get
// object mPages extends MappedInt(this)
// override def pages: Int = mPages.get
// object mPoints extends MappedDouble(this)
// override def points: Double = mPoints.get
// def getSingleton = MappedTemplate
// object mTemplateId extends MappedString(this, 100)
// override def templateId: String = mTemplateId.get
//}
val modelClass = getModelClass(modelTypeName, modelTermName, modelFieldsNames, modelFieldsTypes)
val apiSource: Source = source"""
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
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 Ltd
Osloerstrasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.builder
import java.util.UUID
import code.api.builder.JsonFactory_APIBuilder._
import code.api.util.APIUtil._
import code.api.util.ApiTag._
import code.api.util.ApiVersion
import code.api.util.ErrorMessages._
import net.liftweb.common.Full
import net.liftweb.http.rest.RestHelper
import net.liftweb.json
import net.liftweb.json.Extraction._
import net.liftweb.json._
import net.liftweb.mapper.By
import net.liftweb.util.Helpers.tryo
import scala.collection.immutable.Nil
import scala.collection.mutable.ArrayBuffer
trait APIMethods_APIBuilder
{
self: RestHelper =>
val ImplementationsBuilderAPI = new Object()
{
val apiVersion: ApiVersion = ApiVersion.apiBuilder
val resourceDocs = ArrayBuffer[ResourceDoc]()
val apiRelations = ArrayBuffer[ApiRelation]()
val codeContext = CodeContext(resourceDocs, apiRelations)
implicit val formats = net.liftweb.json.DefaultFormats
$errorMessageVal;
def endpointsOfBuilderAPI = getTemplates :: createTemplate :: getTemplate :: deleteTemplate:: Nil
$getTemplatesResourceCode
$getTemplatesPartialFunction
$createTemplateResourceCode
$createTemplatePartialFunction
$getTemplateResourceCode
$getTemplatePartialFunction
$deleteTemplateResourceCode
$deleteTemplatePartialFunction
}
}
object APIBuilder_Connector
{
val allAPIBuilderModels = List($modelTermName)
$createModelJsonMethod;
def getTemplates()= Full($modelTermName.findAll())
def getTemplateById(templateId: String)= $modelTermName.find(By($modelTermName.mTemplateId, templateId))
def deleteTemplate(templateId: String)= $modelTermName.find(By($modelTermName.mTemplateId, templateId)).map(_.delete_!)
}
import net.liftweb.mapper._
$modelClass
object $modelTermName extends $modelInit with LongKeyedMetaMapper[$modelTypeName] {}
$modelTrait
"""
/*
* ######################################JsonFactory_APIBuilder.scala###################################################
* */
//List(templateId:String = "11231231312" ,author: String = `Chinua Achebe`, tutor: String = `11231231312`, pages: Int = 209, points: Double = 1.3)
//Added the templatedId to `modelCaseClassParams`
val templateJsonClassParams = List(APIBuilderModel.templateIdField)++ modelCaseClassParams
//case class TemplateJson(templateId: String = """1123123 1312""", author: String = """Chinua Achebe""", tutor: String = """1123123 1312""", pages: Int = 209, points: Double = 1.3)
val TemplateJsonClass: Defn.Class = q"""case class TemplateJson(..$templateJsonClassParams) """
//case class Template(author: String = `Chinua Achebe`, pages: Int = 209, points: Double = 1.3)
//Note: No `templateId` in this class, the bank no need provide it, obp create a uuid for it.
val createTemplateJsonClass: Defn.Class = q"""case class CreateTemplateJson(..$modelCaseClassParams) """
//TemplateJson(template.templateId, template.author, template.tutor, template.pages, template.points)
val createTemplateJsonApply: Term.Apply = generateCreateTemplateJsonApply(modelFieldsNames)
//def createTemplate(template: Template) = TemplateJson(template.templateId, template.author, template.tutor, template.pages, template.points)
val createTemplateDef: Defn.Def =q"""def createTemplate(template: Template) = $createTemplateJsonApply"""
//def createTemplates(templates: List[Template]) = templates.map(template => TemplateJson(template.templateId, template.author, template.tutor, template.pages, template.points))
val createTemplatesDef: Defn.Def = q"""def createTemplates(templates: List[Template])= templates.map(template => $createTemplateJsonApply)"""
val jsonFactorySource: Source =source"""
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
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 Ltd
Osloerstrasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.builder
import code.api.util.APIUtil
$TemplateJsonClass
$createTemplateJsonClass
object JsonFactory_APIBuilder{
val templateJson = TemplateJson()
val templatesJson = List(templateJson)
val createTemplateJson = CreateTemplateJson()
$createTemplateDef;
$createTemplatesDef;
val allFields =
for (
v <- this.getClass.getDeclaredFields
//add guard, ignore the SwaggerJSONsV220.this and allFieldsAndValues fields
if (APIUtil.notExstingBaseClass(v.getName()))
)
yield {
v.setAccessible(true)
v.get(this)
}
}
"""
}

View File

@ -0,0 +1,213 @@
{
"openapi": "3.0.0",
"info": {
"title": "Open Bank Project API",
"description": "An Open Source API for Banks. (c) TESOBE Ltd. 2011 - 2018. Licensed under the AGPL and commercial licences.",
"contact": {
"name": "TESOBE Ltd. / Open Bank Project",
"url": "https://openbankproject.com",
"email": "contact@tesobe.com"
},
"version": "b1"
},
"paths": {
"/templates": {
"get": {
"tags": [
"API-Builder"
],
"summary": "Get Templates",
"description": "Return All my templates. Authentication is Mandatory.",
"operationId": "vb1-GetTemplates",
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Templates"
},
"examples": {
"foo": {
"value": {
"templates": [
{
"author": "Chinua Achebe",
"pages": 209,
"points": 1.3
}
]
}
}
}
}
}
},
"400": {
"description": "Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorUserNotLoggedIn"
}
}
}
}
}
},
"post": {
"tags": [
"API-Builder"
],
"summary": "Create Template",
"description": "Create template. Authentication is Mandatory.",
"operationId": "vb1-CreateTemplate",
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Template"
}
}
}
},
"400": {
"description": "Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorUserNotLoggedIn"
}
}
}
}
}
}
},
"/templates/{template_id}": {
"get": {
"tags": [
"API-Builder"
],
"summary": "Get Template",
"description": "Return one template. Authentication is Mandatory.",
"operationId": "vb1-GetTemplate",
"parameters": [
{
"name": "template_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Template"
}
}
}
},
"400": {
"description": "Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorUserNotLoggedIn"
}
}
}
}
}
},
"delete": {
"tags": [
"API-Builder"
],
"summary": "Delete Template",
"description": "Delete template. Authentication is Mandatory.",
"operationId": "vb1-DeleteTemplate",
"parameters": [
{
"name": "template_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success"
},
"400": {
"description": "Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorUserNotLoggedIn"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"ErrorUserNotLoggedIn": {
"properties": {
"message": {
"type": "string",
"example": "OBP-20001: User not logged in. Authentication is required!"
}
}
},
"Templates": {
"required": [
"templates"
],
"properties": {
"templates": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Template"
}
}
}
},
"Template": {
"required": [
"author",
"pages",
"points"
],
"properties": {
"author": {
"type": "string",
"example": "Chinua Achebe"
},
"pages": {
"type": "integer",
"format": "int32",
"example": "209"
},
"points": {
"type": "number",
"format": "double",
"example": "2.99"
}
}
}
}
}
}

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,22 +16,17 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.api
case class ErrorMessage(
error : String
)
case class ErrorMessage(code: Int,
message: String
)

View File

@ -304,7 +304,7 @@ object GatewayLogin extends RestHelper with MdcLoggable {
}
case Full((s, accounts, callContextNew)) if getErrors(s).forall(_.equalsIgnoreCase("")) => // CBS returned response without any error
logger.debug("CBS returned proper response")
Users.users.vend.getOrCreateUserByProviderIdFuture(provider = gateway, idGivenByProvider = username) map {
Users.users.vend.getOrCreateUserByProviderIdFuture(provider = gateway, idGivenByProvider = username, name = None, email = None) map {
case Full(u) =>
val isFirst = getFieldFromPayloadJson(jwtPayload, "is_first")
// Update user account views, only when is_first == true in the GatewayLogin token's payload .

View File

@ -30,6 +30,8 @@ import code.api.util.{APIUtil, CallContext, ErrorMessages, JwtUtil}
import code.model.User
import code.users.Users
import code.util.Helper.MdcLoggable
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet
import net.liftweb.common._
import net.liftweb.http.rest.RestHelper
@ -51,77 +53,209 @@ object OAuth2Handshake extends RestHelper with MdcLoggable {
valueOfAuthReqHeaderField
}
private def verifyJwt(jwt: String) = {
APIUtil.getPropsAsBoolValue("oauth2.jwt.use.ssl", false) match {
case true =>
JwtUtil.verifyRsaSignedJwt(jwt)
case false =>
JwtUtil.verifyHmacSignedJwt(jwt)
}
}
private def validateAccessToken(accessToken: String) = {
APIUtil.getPropsValue("oauth2.jwk_set.url") match {
case Full(url) =>
JwtUtil.validateAccessToken(accessToken, url)
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
ParamFailure(a, b, c, apiFailure : APIFailure)
case Failure(msg, t, c) =>
Failure(msg, t, c)
case _ =>
Failure(ErrorMessages.Oauth2ThereIsNoUrlOfJwkSet)
}
}
/*
Method for Old Style Endpoints
*/
def getUserFromOAuth2Header(sc: CallContext): (Box[User], Option[CallContext]) = {
def getUserFromOAuth2Header(cc: CallContext): (Box[User], Option[CallContext]) = {
APIUtil.getPropsAsBoolValue("allow_oauth2_login", true) match {
case true =>
val value = getValueOfOAuh2HeaderField(sc)
validateAccessToken(value) match {
case Full(_) =>
val username = JwtUtil.getSubject(value).getOrElse("")
(Users.users.vend.getUserByUserName(username), Some(sc))
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
(ParamFailure(a, b, c, apiFailure : APIFailure), Some(sc))
case Failure(msg, t, c) =>
(Failure(msg, t, c), Some(sc))
case _ =>
(Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(sc))
val value = getValueOfOAuh2HeaderField(cc)
if (Google.isIssuer(value)) {
Google.applyRules(value, cc)
} else {
MITREId.applyRules(value, cc)
}
case false =>
(Failure(ErrorMessages.Oauth2IsNotAllowed), Some(sc))
(Failure(ErrorMessages.Oauth2IsNotAllowed), Some(cc))
}
}
/*
Method for New Style Endpoints
*/
def getUserFromOAuth2HeaderFuture(sc: CallContext): Future[(Box[User], Option[CallContext])] = {
def getUserFromOAuth2HeaderFuture(cc: CallContext): Future[(Box[User], Option[CallContext])] = {
APIUtil.getPropsAsBoolValue("allow_oauth2_login", true) match {
case true =>
val value = getValueOfOAuh2HeaderField(sc)
validateAccessToken(value) match {
case Full(_) =>
val username = JwtUtil.getSubject(value).getOrElse("")
(Users.users.vend.getUserByUserName(username), Some(sc))
for {
user <- Users.users.vend.getUserByUserNameFuture(username)
} yield {
(user, Some(sc))
}
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
Future((ParamFailure(a, b, c, apiFailure : APIFailure), Some(sc)))
case Failure(msg, t, c) =>
Future((Failure(msg, t, c), Some(sc)))
case _ =>
Future((Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(sc)))
val value = getValueOfOAuh2HeaderField(cc)
if (Google.isIssuer(value)) {
Google.applyRulesFuture(value, cc)
} else {
MITREId.applyRulesFuture(value, cc)
}
case false =>
Future((Failure(ErrorMessages.Oauth2IsNotAllowed), Some(sc)))
Future((Failure(ErrorMessages.Oauth2IsNotAllowed), Some(cc)))
}
}
object MITREId {
def validateAccessToken(accessToken: String): Box[JWTClaimsSet] = {
APIUtil.getPropsValue("oauth2.jwk_set.url") match {
case Full(url) =>
JwtUtil.validateAccessToken(accessToken, url)
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
ParamFailure(a, b, c, apiFailure : APIFailure)
case Failure(msg, t, c) =>
Failure(msg, t, c)
case _ =>
Failure(ErrorMessages.Oauth2ThereIsNoUrlOfJwkSet)
}
}
def applyRules(value: String, cc: CallContext): (Box[User], Some[CallContext]) = {
validateAccessToken(value) match {
case Full(_) =>
val username = JwtUtil.getSubject(value).getOrElse("")
(Users.users.vend.getUserByUserName(username), Some(cc))
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
(ParamFailure(a, b, c, apiFailure : APIFailure), Some(cc))
case Failure(msg, t, c) =>
(Failure(msg, t, c), Some(cc))
case _ =>
(Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(cc))
}
}
def applyRulesFuture(value: String, cc: CallContext): Future[(Box[User], Some[CallContext])] = {
validateAccessToken(value) match {
case Full(_) =>
val username = JwtUtil.getSubject(value).getOrElse("")
for {
user <- Users.users.vend.getUserByUserNameFuture(username)
} yield {
(user, Some(cc))
}
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
Future((ParamFailure(a, b, c, apiFailure : APIFailure), Some(cc)))
case Failure(msg, t, c) =>
Future((Failure(msg, t, c), Some(cc)))
case _ =>
Future((Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(cc)))
}
}
}
trait OAuth2Util {
def urlOfJwkSets: Box[String] = APIUtil.getPropsValue(nameOfProperty = "oauth2.jwk_set.url")
private def getClaim(name: String, idToken: String): Option[String] = {
val claim = JwtUtil.getClaim(name = name, jwtToken = idToken).asString()
claim match {
case null => None
case string => Some(string)
}
}
def isIssuer(jwtToken: String): Boolean = {
JwtUtil.getIssuer(jwtToken).map(_.contains("accounts.google.com")).getOrElse(false)
}
def validateIdToken(idToken: String): Box[IDTokenClaimsSet] = {
urlOfJwkSets match {
case Full(url) =>
JwtUtil.validateIdToken(idToken, url)
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
ParamFailure(a, b, c, apiFailure : APIFailure)
case Failure(msg, t, c) =>
Failure(msg, t, c)
case _ =>
Failure(ErrorMessages.Oauth2ThereIsNoUrlOfJwkSet)
}
}
/** New Style Endpoints
* This function creates user based on "iss" and "sub" fields
* It is mapped in next way:
* iss => ResourceUser.provider_
* sub => ResourceUser.providerId
* @param idToken Google's response example:
* {
* "access_token": "ya29.GluUBg5DflrJciFikW5hqeKEp9r1whWnU5x2JXCm9rKkRMs2WseXX8O5UugFMDsIKuKCZlE7tTm1fMII_YYpvcMX6quyR5DXNHH8Lbx5TrZN__fA92kszHJEVqPc",
* "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjA4ZDMyNDVjNjJmODZiNjM2MmFmY2JiZmZlMWQwNjk4MjZkZDFkYzEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJlbWFpbCI6Im1hcmtvLm1pbGljLnNyYmlqYUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6Im5HS1JUb0tOblZBMjhINk1od1hCeHciLCJuYW1lIjoiTWFya28gTWlsacSHIiwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tWGQ0NGhuSjZURG8vQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQUt4cndjYWR3emhtNE40dFdrNUU4QXZ4aS1aSzZrczRxZy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiTWFya28iLCJmYW1pbHlfbmFtZSI6Ik1pbGnEhyIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTQ3NzA1NjkxLCJleHAiOjE1NDc3MDkyOTF9.iUxhF_SU2vi76zPuRqAKJvFOzpb_EeP3lc5u9FO9o5xoXzVq3QooXexTfK2f1YAcWEy9LSftA34PB0QTuCZpkQChZVM359n3a3hplf6oWWkBXZN2_IG10NwEH4g0VVBCsjWBDMp6lvepN_Zn15x8opUB7272m4-smAou_WmUPTeivXRF8yPcp4J55DigcY31YP59dMQr2X-6Rr1vCRnJ6niqqJ1UDldfsgt4L7dXmUCnkDdXHwEQAZwbKbR4dUoEha3QeylCiBErmLdpIyqfKECphC6piGXZB-rRRqLz41WNfuF-3fswQvGmIkzTJDR7lQaletMp7ivsfVw8N5jFxg",
* "expires_in": 3600,
* "token_type": "Bearer",
* "scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
* "refresh_token": "1/HkTtUahtUTdG7D6urpPNz6g-_qufF-Y1YppcBf0v3Cs"
* }
* @return an existing or a new user
*/
def getOrCreateResourceUserFuture(idToken: String): Future[Box[User]] = {
val subject = JwtUtil.getSubject(idToken).getOrElse("")
val issuer = JwtUtil.getIssuer(idToken).getOrElse("")
Users.users.vend.getOrCreateUserByProviderIdFuture(
provider = issuer,
idGivenByProvider = subject,
name = getClaim(name = "name", idToken = idToken).orElse(Some(subject)),
email = getClaim(name = "email", idToken = idToken)
)
}
/** Old Style Endpoints
* This function creates user based on "iss" and "sub" fields
* It is mapped in next way:
* iss => ResourceUser.provider_
* sub => ResourceUser.providerId
* @param idToken Google's response example:
* {
* "access_token": "ya29.GluUBg5DflrJciFikW5hqeKEp9r1whWnU5x2JXCm9rKkRMs2WseXX8O5UugFMDsIKuKCZlE7tTm1fMII_YYpvcMX6quyR5DXNHH8Lbx5TrZN__fA92kszHJEVqPc",
* "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjA4ZDMyNDVjNjJmODZiNjM2MmFmY2JiZmZlMWQwNjk4MjZkZDFkYzEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJlbWFpbCI6Im1hcmtvLm1pbGljLnNyYmlqYUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6Im5HS1JUb0tOblZBMjhINk1od1hCeHciLCJuYW1lIjoiTWFya28gTWlsacSHIiwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tWGQ0NGhuSjZURG8vQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQUt4cndjYWR3emhtNE40dFdrNUU4QXZ4aS1aSzZrczRxZy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiTWFya28iLCJmYW1pbHlfbmFtZSI6Ik1pbGnEhyIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTQ3NzA1NjkxLCJleHAiOjE1NDc3MDkyOTF9.iUxhF_SU2vi76zPuRqAKJvFOzpb_EeP3lc5u9FO9o5xoXzVq3QooXexTfK2f1YAcWEy9LSftA34PB0QTuCZpkQChZVM359n3a3hplf6oWWkBXZN2_IG10NwEH4g0VVBCsjWBDMp6lvepN_Zn15x8opUB7272m4-smAou_WmUPTeivXRF8yPcp4J55DigcY31YP59dMQr2X-6Rr1vCRnJ6niqqJ1UDldfsgt4L7dXmUCnkDdXHwEQAZwbKbR4dUoEha3QeylCiBErmLdpIyqfKECphC6piGXZB-rRRqLz41WNfuF-3fswQvGmIkzTJDR7lQaletMp7ivsfVw8N5jFxg",
* "expires_in": 3600,
* "token_type": "Bearer",
* "scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
* "refresh_token": "1/HkTtUahtUTdG7D6urpPNz6g-_qufF-Y1YppcBf0v3Cs"
* }
* @return an existing or a new user
*/
def getOrCreateResourceUser(idToken: String): Box[User] = {
val subject = JwtUtil.getSubject(idToken).getOrElse("")
val issuer = JwtUtil.getIssuer(idToken).getOrElse("")
Users.users.vend.getUserByProviderId(provider = issuer, idGivenByProvider = subject).or { // Find a user
Users.users.vend.createResourceUser( // Otherwise create a new one
provider = issuer,
providerId = Some(subject),
name = getClaim(name = "name", idToken = idToken).orElse(Some(subject)),
email = getClaim(name = "email", idToken = idToken),
userId = None
)
}
}
def applyRules(value: String, cc: CallContext): (Box[User], Some[CallContext]) = {
validateIdToken(value) match {
case Full(_) =>
val user = Google.getOrCreateResourceUser(value)
(user, Some(cc))
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
(ParamFailure(a, b, c, apiFailure : APIFailure), Some(cc))
case Failure(msg, t, c) =>
(Failure(msg, t, c), Some(cc))
case _ =>
(Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(cc))
}
}
def applyRulesFuture(value: String, cc: CallContext): Future[(Box[User], Some[CallContext])] = {
validateIdToken(value) match {
case Full(_) =>
for {
user <- Google.getOrCreateResourceUserFuture(value)
} yield {
(user, Some(cc))
}
case ParamFailure(a, b, c, apiFailure : APIFailure) =>
Future((ParamFailure(a, b, c, apiFailure : APIFailure), Some(cc)))
case Failure(msg, t, c) =>
Future((Failure(msg, t, c), Some(cc)))
case _ =>
Future((Failure(ErrorMessages.Oauth2IJwtCannotBeVerified), Some(cc)))
}
}
}
object Google extends OAuth2Util {
override def urlOfJwkSets: Box[String] = {
val url = APIUtil.getPropsValue(nameOfProperty = "oauth2.jwk_set.url")
url.map(_.toLowerCase()).map(_.contains("google")).getOrElse(false) match {
case true => url
case false => Failure(ErrorMessages.Oauth2CannotMatchIssuerAndJwksUriException)
}
}
}
}

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,17 +16,12 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/

View File

@ -21,6 +21,7 @@ import net.liftweb.http.rest.RestHelper
import net.liftweb.http.{JsonResponse, LiftRules, S}
import net.liftweb.json.JsonAST.{JField, JString, JValue}
import net.liftweb.json._
import net.liftweb.util.Helpers.tryo
import net.liftweb.util.Props
import scala.collection.immutable.Nil
@ -65,6 +66,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
{
// Determine if the partialFunctionName is due to be "featured" in API Explorer etc.
// "Featured" means shown at the top of the list or so.
def getIsFeaturedApi(partialFunctionName: String) : Boolean = {
val partialFunctionNames = APIUtil.getPropsValue("featured_apis") match {
case Full(v) =>
@ -121,6 +123,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
case ApiVersion.v1_4_0 => Implementations1_4_0.resourceDocs ++ Implementations1_3_0.resourceDocs ++ Implementations1_2_1.resourceDocs
case ApiVersion.v1_3_0 => Implementations1_3_0.resourceDocs ++ Implementations1_2_1.resourceDocs
case ApiVersion.v1_2_1 => Implementations1_2_1.resourceDocs
case _ => ArrayBuffer.empty[ResourceDoc]
}
logger.debug(s"There are ${resourceDocs.length} resource docs available to $requestedApiVersion")
@ -137,6 +140,7 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
case ApiVersion.v1_4_0 => OBPAPI1_4_0.routes
case ApiVersion.v1_3_0 => OBPAPI1_3_0.routes
case ApiVersion.v1_2_1 => OBPAPI1_2_1.routes
case _ => Nil
}
logger.debug(s"There are ${versionRoutes.length} routes available to $requestedApiVersion")
@ -160,6 +164,10 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
// Add any featured status and special instructions from Props
// Overwrite the requestUrl adding /obp
// TODO We ideally need to return two urls here:
// 1) the implemented in url i.e. the earliest version under which this endpoint is available
// 2) the called url (contains the version we are calling)
val theResourceDocs = for {
x <- activePlusLocalResourceDocs
// This is the "implemented in" url
@ -172,7 +180,17 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
}
y = x.copy(isFeatured = getIsFeaturedApi(x.partialFunctionName),
specialInstructions = getSpecialInstructions(x.partialFunctionName),
requestUrl = url)
requestUrl = url,
specifiedUrl = x.implementedInApiVersion match {
case ApiVersion.`berlinGroupV1` => Some(url)
case ApiVersion.`ukOpenBankingV200` => Some(url)
case ApiVersion.`apiBuilder` => Some(url)
// We add the /obp/vX prefix here - but this is the requested API version by the resource docs endpoint. i.e. we know this endpoint
// is also available here as well as the requestUrl. See the resource doc for resource doc!
case _ => Some(s"/obp/${requestedApiVersion.vDottedApiVersion}${x.requestUrl}")
}
)
} yield y
@ -215,10 +233,10 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
Caching.memoizeSyncWithProvider (Some(cacheKey.toString())) (getResourceDocsTTL millisecond) {
logger.debug(s"Generating OBP Resource Docs showCore is $showCore showPSD2 is $showPSD2 showOBWG is $showOBWG requestedApiVersion is $requestedApiVersion")
val obpResourceDocJson = for {
rd <- getResourceDocsList(requestedApiVersion)
resourceDocs <- getResourceDocsList(requestedApiVersion)
} yield {
// Filter
val rdFiltered = filterResourceDocs(rd, showCore, showPSD2, showOBWG, resourceDocTags, partialFunctionNames)
val rdFiltered = filterResourceDocs(resourceDocs, showCore, showPSD2, showOBWG, resourceDocTags, partialFunctionNames)
// Format the data as json
val innerJson = JSONFactory1_4_0.createResourceDocsJson(rdFiltered)
// Return
@ -392,7 +410,8 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
|<li> operation_id is concatenation of "v", version and function and should be unique (used for DOM element IDs etc. maybe used to link to source code) </li>
|<li> version references the version that the API call is defined in.</li>
|<li> function is the (scala) partial function that implements this endpoint. It is unique per version of the API.</li>
|<li> request_url is empty for the root call, else the path.</li>
|<li> request_url is empty for the root call, else the path. It contains the standard prefix (e.g. /obp) and the implemented version (the version where this endpoint was defined) e.g. /obp/v1.2.0/resource</li>
|<li> specified_url (recommended to use) is empty for the root call, else the path. It contains the standard prefix (e.g. /obp) and the version specified in the call e.g. /obp/v3.1.0/resource. In OBP, endpoints are first made available at the request_url, but the same resource (function call) is often made available under later versions (specified_url). To access the latest version of all endpoints use the latest version available on your OBP instance e.g. /obp/v3.1.0 - To get the original version use the request_url. We recommend to use the specified_url since non semantic improvements are more likely to be applied to later implementations of the call.</li>
|<li> summary is a short description inline with the swagger terminology. </li>
|<li> description may contain html markup (generated from markdown on the server).</li>
|</ul>
@ -409,15 +428,13 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
def getResourceDocsObp : OBPEndpoint = {
case "resource-docs" :: requestedApiVersionString :: "obp" :: Nil JsonGet _ => {
val (showCore, showPSD2, showOBWG, tags, partialFunctions) = getParams()
cc =>{
for {
requestedApiVersion <- Full(ApiVersion.valueOf(requestedApiVersionString)) ?~! InvalidApiVersionString
_ <- booleanToBox(versionIsAllowed(requestedApiVersion), ApiVersionNotSupported)
(showCore, showPSD2, showOBWG, tags, partialFunctions) <- Full(getParams())
requestedApiVersion <- tryo {ApiVersion.valueOf(requestedApiVersionString)} ?~! s"$InvalidApiVersionString Current Version is $requestedApiVersionString"
_ <- booleanToBox(versionIsAllowed(requestedApiVersion), s"$ApiVersionNotSupported Current Version is $requestedApiVersionString")
json <- getResourceDocsObpCached(showCore, showPSD2, showOBWG, requestedApiVersion, tags, partialFunctions)
}
yield {
} yield {
json
}
}
@ -456,9 +473,17 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth
def getResourceDocsSwagger : OBPEndpoint = {
case "resource-docs" :: requestedApiVersion :: "swagger" :: Nil JsonGet _ => {
val (showCore, showPSD2, showOBWG, resourceDocTags, partialFunctions) = getParams()
cc =>getResourceDocsSwaggerCached(showCore, showPSD2, showOBWG, requestedApiVersion, resourceDocTags, partialFunctions)
case "resource-docs" :: requestedApiVersionString :: "swagger" :: Nil JsonGet _ => {
cc =>{
for {
(showCore, showPSD2, showOBWG, resourceDocTags, partialFunctions) <- tryo(getParams())
requestedApiVersion <- tryo(ApiVersion.valueOf(requestedApiVersionString)) ?~! s"$InvalidApiVersionString Current Version is $requestedApiVersionString"
_ <- booleanToBox(versionIsAllowed(requestedApiVersion), s"$ApiVersionNotSupported Current Version is $requestedApiVersionString")
json <- getResourceDocsSwaggerCached(showCore, showPSD2, showOBWG, requestedApiVersionString, resourceDocTags, partialFunctions)
} yield {
json
}
}
}
}

View File

@ -398,7 +398,8 @@ object SwaggerDefinitionsJSON {
other_account_routing_address= "String",
other_bank_routing_scheme= "String",
other_bank_routing_address= "String",
is_beneficiary= true
is_beneficiary= true,
future_date = Some("20881230")
)
@ -457,7 +458,8 @@ object SwaggerDefinitionsJSON {
)*/
val errorMessage = ErrorMessage(
error = "String"
code = 500,
message = "Internal Server Error"
)
val postTransactionImageJSON = PostTransactionImageJSON(
@ -1418,7 +1420,8 @@ object SwaggerDefinitionsJSON {
typed_success_response_body = json.parse("""{"response": { "type" :"string" }}"""),
roles = Some(List(canCreateCustomerSwagger)),
is_featured = false,
special_instructions = ""
special_instructions = "",
specified_url = ""
)
val resourceDocsJson = ResourceDocsJson(resource_docs = List(resourceDocJson))
@ -1863,14 +1866,16 @@ object SwaggerDefinitionsJSON {
counterpartyIdJson,
amountOfMoneyJsonV121,
"A description for the transaction to the counterparty",
"SHARED"
"SHARED",
Some("20881230")
)
val transactionRequestBodySEPAJSON = TransactionRequestBodySEPAJSON(
amountOfMoneyJsonV121,
ibanJson,
"This is a SEPA Transaction Request",
"SHARED"
"SHARED",
Some("20881230")
)
val customerCreditRatingJSON = CustomerCreditRatingJSON(
@ -2931,6 +2936,7 @@ object SwaggerDefinitionsJSON {
trigger_name = ApiTrigger.onBalanceChange.toString(),
url = "https://localhost.openbankproject.com",
http_method = "POST",
http_protocol = "HTTP/1.1",
is_active = "true"
)
val accountWebhookPutJson = AccountWebhookPutJson(
@ -2944,6 +2950,7 @@ object SwaggerDefinitionsJSON {
trigger_name = ApiTrigger.onBalanceChange.toString(),
url = "https://localhost.openbankproject.com",
http_method = "POST",
http_protocol = "HTTP/1.1",
created_by_user_id = "b1fd9b29-659d-4838-a300-ea65b65b5fb6",
is_active = true
)
@ -2985,6 +2992,26 @@ object SwaggerDefinitionsJSON {
value = "2012-04-23"
)
val accountApplicationJson = AccountApplicationJson(
product_code = "saveing1",
user_id = Some("123"),
customer_id = Some("123")
)
val accountApplicationResponseJson = AccountApplicationResponseJson (
account_application_id = "gc23a7e2-7dd2-4bdf-a0b4-ae31232a4763",
product_code = "saveing1",
user = resourceUserJSON,
customer = customerJsonV310,
date_of_application = DateWithDayExampleObject,
status = "REQUESTED"
)
val accountApplicationUpdateStatusJson = AccountApplicationUpdateStatusJson(
status = "ACCEPTED"
)
val accountApplicationsJsonV310 = AccountApplicationsJsonV310(List(accountApplicationResponseJson))
//The common error or success format.
//Just some helper format to use in Json
case class NoSupportYet()

View File

@ -213,7 +213,7 @@ object SwaggerJSONFactory {
// "400": {
// "description": "Error",
// "schema": {"$ref": "#/definitions/Error"
val paths: ListMap[String, Map[String, OperationObjectJson]] = resourceDocList.groupBy(x => x.requestUrl).toSeq.sortBy(x => x._1).map { mrd =>
val paths: ListMap[String, Map[String, OperationObjectJson]] = resourceDocList.groupBy(x => x.specifiedUrl.getOrElse(x.requestUrl)).toSeq.sortBy(x => x._1).map { mrd =>
//`/banks/BANK_ID` --> `/obp/v3.0.0/banks/BANK_ID`
val pathAddedObpandVersion = mrd._1

View File

@ -1,33 +1,28 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2018, TESOBE Ltd
**
*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 Ltd
*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
*Stefan Bethge : stefan AT tesobe DOT com
*Everett Sochowski : everett AT tesobe DOT com
*Ayoub Benali: ayoub AT tesobe DOT com
*
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd.
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 Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.UKOpenBanking.v2_0_0

View File

@ -1,33 +1,28 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2018, TESOBE Ltd
**
*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 Ltd
*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
*Stefan Bethge : stefan AT tesobe DOT com
*Everett Sochowski : everett AT tesobe DOT com
*Ayoub Benali: ayoub AT tesobe DOT com
*
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd.
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 Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.berlin.group.v1

View File

@ -28,14 +28,14 @@ package code.api
import java.util.Date
import authentikat.jwt.{JsonWebToken, JwtClaimsSet, JwtHeader}
import code.api.util.APIUtil._
import code.api.util.{APIUtil, CallContext, ErrorMessages}
import code.api.util._
import code.consumer.Consumers._
import code.model.dataAccess.AuthUser
import code.model.{Consumer, Token, TokenType, User}
import code.token.Tokens
import code.util.Helper.{MdcLoggable, SILENCE_IS_GOLDEN}
import com.nimbusds.jwt.JWTClaimsSet
import net.liftweb.common._
import net.liftweb.http._
import net.liftweb.http.rest.RestHelper
@ -106,8 +106,13 @@ object DirectLogin extends RestHelper with MdcLoggable {
message = ErrorMessages.UsernameHasBeenLocked
httpCode = 401
} else {
val claims = Map("" -> "")
val (token:String, secret:String) = generateTokenAndSecret(claims)
val jwtPayloadAsJson =
"""{
"":""
}"""
val jwtClaims: JWTClaimsSet = JWTClaimsSet.parse(jwtPayloadAsJson)
val (token:String, secret:String) = generateTokenAndSecret(jwtClaims)
//Save the token that we have generated
if (saveAuthorizationToken(directLoginParameters, token, secret, userId)) {
@ -406,14 +411,12 @@ object DirectLogin extends RestHelper with MdcLoggable {
}
private def generateTokenAndSecret(claims: Map[String,String]) =
private def generateTokenAndSecret(claims: JWTClaimsSet): (String, String) =
{
// generate random string
val secret_message = Helpers.randomString(40)
// jwt header
val header = JwtHeader("HS256")
// generate jwt token
val token_message = JsonWebToken(header, JwtClaimsSet(claims), secret_message)
val token_message = CertificateUtil.jwtWithHmacProtection(claims, secret_message)
(token_message, secret_message)
}

View File

@ -33,7 +33,7 @@ import code.api.Constant._
import code.api.oauth1a.Arithmetics
import code.api.oauth1a.OauthParams._
import code.api.util.ErrorMessages._
import code.api.util.{APIUtil, CallContext, ErrorMessages}
import code.api.util._
import code.consumer.Consumers
import code.model.{Consumer, TokenType, User}
import code.nonce.Nonces

View File

@ -1,34 +1,29 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2018, TESOBE Ltd
**
*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 Ltd
*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
*Stefan Bethge : stefan AT tesobe DOT com
*Everett Sochowski : everett AT tesobe DOT com
*Ayoub Benali: ayoub AT tesobe DOT com
*
*/
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd.
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 Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.util
@ -79,6 +74,7 @@ import scala.collection.immutable.Nil
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.reflect.ClassTag
import scala.xml.{Elem, XML}
object APIUtil extends MdcLoggable {
@ -427,15 +423,15 @@ object APIUtil extends MdcLoggable {
case _ =>
httpCode
}
JsonResponse(Extraction.decompose(ErrorMessage(message)), getHeaders() ::: headers.list, Nil, code)
JsonResponse(Extraction.decompose(ErrorMessage(message = message, code = code)), getHeaders() ::: headers.list, Nil, code)
}
def notImplementedJsonResponse(message : String = ErrorMessages.NotImplemented, httpCode : Int = 501)(implicit headers: CustomResponseHeaders = CustomResponseHeaders(Nil)) : JsonResponse =
JsonResponse(Extraction.decompose(ErrorMessage(message)), getHeaders() ::: headers.list, Nil, httpCode)
JsonResponse(Extraction.decompose(ErrorMessage(message = message, code = httpCode)), getHeaders() ::: headers.list, Nil, httpCode)
def oauthHeaderRequiredJsonResponse(implicit headers: CustomResponseHeaders = CustomResponseHeaders(Nil)) : JsonResponse =
JsonResponse(Extraction.decompose(ErrorMessage("Authentication via OAuth is required")), getHeaders() ::: headers.list, Nil, 400)
JsonResponse(Extraction.decompose(ErrorMessage(message = "Authentication via OAuth is required", code = 400)), getHeaders() ::: headers.list, Nil, 400)
/** check the currency ISO code from the ISOCurrencyCodes.xml file */
def isValidCurrencyISOCode(currencyCode: String): Boolean = {
@ -458,6 +454,10 @@ object APIUtil extends MdcLoggable {
}
}
/** enforce the password.
* The rules :
* 1) length is >16 characters without validations
@ -1032,7 +1032,7 @@ object APIUtil extends MdcLoggable {
implementedInApiVersion: ApiVersion, // TODO: Use ApiVersion enumeration instead of string
partialFunctionName: String, // The string name of the partial function that implements this resource. Could use it to link to the source code that implements the call
requestVerb: String, // GET, POST etc. TODO: Constrain to GET, POST etc.
requestUrl: String, // The URL (includes implemented in prefix e.g. /obp/vX.X). Starts with / No trailing slash. TODO Constrain the string?
requestUrl: String, // The URL. THIS GETS MODIFIED TO include the implemented in prefix e.g. /obp/vX.X). Starts with / No trailing slash.
summary: String, // A summary of the call (originally taken from code comment) SHOULD be under 120 chars to be inline with Swagger
description: String, // Longer description (originally taken from github wiki)
exampleRequestBody: scala.Product, // An example of the body required (maybe empty)
@ -1042,7 +1042,8 @@ object APIUtil extends MdcLoggable {
tags: List[ResourceDocTag],
roles: Option[List[ApiRole]] = None,
isFeatured: Boolean = false,
specialInstructions: Option[String] = None
specialInstructions: Option[String] = None,
specifiedUrl: Option[String] = None // A derived value: Contains the called version (added at run time). See the resource doc for resource doc!
)
@ -1297,6 +1298,20 @@ Returns a string showed to the developer
def hasScope(bankId: String, consumerId: String, role: ApiRole): Boolean = {
!Scope.scope.vend.getScope(bankId, consumerId, role.toString).isEmpty
}
def getConsumerPrimaryKey(callContext: Option[CallContext]): String = {
callContext match {
case Some(cc) =>
cc.consumer.map(_.id.get.toString).getOrElse("")
case _ =>
""
}
}
def checkScope(bankId: String, consumerId: String, role: ApiRole): Boolean = {
REQUIRE_SCOPES match {
case false => true // if the props require_scopes == false, we do not need to check the Scope stuff..
case true => !Scope.scope.vend.getScope(bankId, consumerId, role.toString).isEmpty
}
}
// Function checks does a consumer specified by a parameter consumerId has at least one role provided by a parameter roles at a bank specified by a parameter bankId
// i.e. does consumer has assigned at least one role from the list
@ -1388,11 +1403,12 @@ Returns a string showed to the developer
def saveConnectorMetric[R](blockOfCode: => R)(nameOfFunction: String = "")(implicit nameOfConnector: String): R = {
val t0 = System.currentTimeMillis()
// call-by-name
val result = blockOfCode
val t1 = System.currentTimeMillis()
if (getPropsAsBoolValue("write_connector_metrics", false)){
val t0 = System.currentTimeMillis()
// call-by-name
val t1 = System.currentTimeMillis()
val correlationId = getCorrelationId()
Future {
ConnectorMetricsProvider.metrics.vend.saveConnectorMetric(nameOfConnector, nameOfFunction, correlationId, now, t1 - t0)
@ -1641,6 +1657,7 @@ Returns a string showed to the developer
case ApiVersion.`berlinGroupV1` => LiftRules.statelessDispatch.append(OBP_BERLIN_GROUP_1)
case ApiVersion.`ukOpenBankingV200` => LiftRules.statelessDispatch.append(OBP_UKOpenBanking_200)
case ApiVersion.`apiBuilder` => LiftRules.statelessDispatch.append(OBP_APIBuilder)
case _ => logger.info(s"There is no ${version.toString}")
}
logger.info(s"${version.toString} was ENABLED")
@ -1793,6 +1810,7 @@ Returns a string showed to the developer
t => Full(logEndpointTiming(t._2.map(_.toLight))(reply.apply(successJsonResponseNewStyle(t._1, t._2)(getHeadersNewStyle(t._2.map(_.toLight))))))
)
in.onFail {
case Failure(null, _, _) => Full(reply.apply(errorJsonResponse(UnknownError)))
case Failure(msg, _, _) =>
extractAPIFailureNewStyle(msg) match {
case Some(af) =>
@ -1801,7 +1819,7 @@ Returns a string showed to the developer
Full((reply.apply(errorJsonResponse(msg))))
}
case _ =>
Full(reply.apply(errorJsonResponse("Error")))
Full(reply.apply(errorJsonResponse(UnknownError)))
}
})
}
@ -1935,6 +1953,20 @@ Returns a string showed to the developer
}
}
def unboxFuture[T](box: Box[Future[T]]): Future[Box[T]] = box match {
case Full(v) => v.map(Box !! _)
case other => Future(other.asInstanceOf[Box[T]])
}
def unboxOBPReturnType[T](box: Box[OBPReturnType[T]]): Future[Box[T]] = box match {
case Full(v) => v.map(Box !! _._1)
case other => Future(other.asInstanceOf[Box[T]])
}
def unboxOptionFuture[T](option: Option[Future[T]]): Future[Box[T]] = unboxFuture(Box(option))
def unboxOptionOBPReturnType[T](option: Option[OBPReturnType[T]]): Future[Box[T]] = unboxOBPReturnType(Box(option))
/**
* This function checks rate limiting for a Consumer.
@ -2167,7 +2199,13 @@ Returns a string showed to the developer
* @return Decrypted value of a property
*/
def getPropsValue(nameOfProperty: String): Box[String] = {
(Props.get(nameOfProperty), Props.get(nameOfProperty + ".is_encrypted"), Props.get(nameOfProperty + ".is_obfuscated") ) match {
val brandSpecificPropertyName = getBrandSpecificPropertyName(nameOfProperty)
logger.debug(s"Standard property $nameOfProperty has bankSpecificPropertyName: $brandSpecificPropertyName")
(Props.get(brandSpecificPropertyName), Props.get(brandSpecificPropertyName + ".is_encrypted"), Props.get(brandSpecificPropertyName + ".is_obfuscated") ) match {
case (Full(base64PropsValue), Full(isEncrypted), Empty) if isEncrypted == "true" =>
val decryptedValueAsString = RSAUtil.decrypt(base64PropsValue)
Full(decryptedValueAsString)
@ -2182,8 +2220,8 @@ Returns a string showed to the developer
case (Empty, Empty, Empty) =>
Empty
case _ =>
logger.error(cannotDecryptValueOfProperty + nameOfProperty)
Failure(cannotDecryptValueOfProperty + nameOfProperty)
logger.error(cannotDecryptValueOfProperty + brandSpecificPropertyName)
Failure(cannotDecryptValueOfProperty + brandSpecificPropertyName)
}
}
def getPropsValue(nameOfProperty: String, defaultValue: String): String = {
@ -2206,6 +2244,58 @@ Returns a string showed to the developer
getPropsAsLongValue(nameOfProperty) openOr(defaultValue)
}
/*
Get any brand specified in url parameter or form field, validate it, and if all good, set the session
Else just return the session
Note there are Read and Write side effects here!
*/
def activeBrand() : Option[String] = {
val brandParameter = "brand"
// Use brand in parameter (query or form)
val brand : Option[String] = S.param(brandParameter) match {
case Full(value) => {
// If found, and has a valid format, set the session.
if (isValidID(value)) {
S.setSessionAttribute(brandParameter, value)
logger.debug(s"activeBrand says: I found a $brandParameter param. $brandParameter session has been set to: ${S.getSessionAttribute(brandParameter)}")
Some(value)
} else {
logger.warn (s"activeBrand says: ${ErrorMessages.InvalidBankIdFormat}")
None
}
}
case _ => {
// Else look in the session
S.getSessionAttribute(brandParameter)
}
}
brand
}
/*
For bank specific branding and possibly other customisations, if we have an active brand (in url param, form field, session),
we will look for property_FOR_BRAND_<BANK_ID>
We also check that the property exists, else return the standard property name.
*/
def getBrandSpecificPropertyName(nameOfProperty: String) : String = {
// If we have an active brand, construct a target property name to look for.
val brandSpecificPropertyName = activeBrand() match {
case Some(brand) => s"${nameOfProperty}_FOR_BRAND_${brand}"
case _ => nameOfProperty
}
// Check if the property actually exits, if not, return the default / standard property name
val propertyToUse = Props.get(brandSpecificPropertyName) match {
case Full(value) => brandSpecificPropertyName
case _ => nameOfProperty
}
propertyToUse
}
val ALLOW_PUBLIC_VIEWS: Boolean = getPropsAsBoolValue("allow_public_views", false)

View File

@ -213,7 +213,16 @@ object ApiRole {
case class CanRefreshUser(requiresBankId: Boolean = false) extends ApiRole
lazy val canRefreshUser = CanRefreshUser()
case class CanGetAccountApplications(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetAccountApplications = CanGetAccountApplications()
case class CanUpdateAccountApplications(requiresBankId: Boolean = false) extends ApiRole
lazy val canUpdateAccountApplications = CanUpdateAccountApplications()
case class CanReadFx(requiresBankId: Boolean = true) extends ApiRole
lazy val canReadFx = CanReadFx()
private val roles =
canSearchAllTransactions ::
canSearchAllAccounts ::
@ -284,6 +293,9 @@ object ApiRole {
canCreateTaxResidence ::
canDeleteTaxResidence ::
canRefreshUser ::
canGetAccountApplications::
canUpdateAccountApplications::
canReadFx::
Nil
lazy val rolesMappedToClasses = roles.map(_.getClass)

View File

@ -34,6 +34,17 @@ case class CallContext(
`X-Rate-Limit-Remaining` : Long = -1,
`X-Rate-Limit-Reset` : Long = -1
) {
def removeResourceDocument: CallContext = this.copy(resourceDocument = None)
def toCallContextAkka: CallContextAkka =
CallContextAkka(
userId = this.user.map(_.userId).toOption,
consumerId = this.consumer.map(_.consumerId.get).toOption,
correlationId = this.correlationId,
sessionId = this.sessionId
)
def toLight: CallContextLight = {
CallContextLight(
gatewayLoginRequestPayload = this.gatewayLoginRequestPayload,
@ -86,6 +97,12 @@ case class CallContextLight(gatewayLoginRequestPayload: Option[PayloadOfJwtJSON]
`X-Rate-Limit-Reset` : Long = -1
)
case class CallContextAkka(userId: Option[String] = None,
consumerId: Option[String] = None,
correlationId: String = "",
sessionId: Option[String] = None,
)
trait GatewayLoginParam
case class GatewayLoginRequestPayload(jwtPayload: Option[PayloadOfJwtJSON]) extends GatewayLoginParam
case class GatewayLoginResponseHeader(jwt: Option[String]) extends GatewayLoginParam

View File

@ -10,6 +10,7 @@ object ApiTag {
val apiTagApi = ResourceDocTag("API")
val apiTagBank = ResourceDocTag("Bank")
val apiTagAccount = ResourceDocTag("Account")
val apiTagAccountApplication = ResourceDocTag("Account-Application")
val apiTagAccountPublic = ResourceDocTag("Account-Public")
val apiTagAccountFirehose = ResourceDocTag("Account-Firehose")
val apiTagFirehoseData = ResourceDocTag("FirehoseData")
@ -19,9 +20,10 @@ object ApiTag {
val apiTagTransactionFirehose = ResourceDocTag("Transaction-Firehose")
val apiTagCounterpartyMetaData = ResourceDocTag("Counterparty-Metadata")
val apiTagTransactionMetaData = ResourceDocTag("Transaction-Metadata")
val apiTagView = ResourceDocTag("Account-View")
val apiTagView = ResourceDocTag("View")
val apiTagEntitlement = ResourceDocTag("Entitlement")
val apiTagRole = ResourceDocTag("API-Role")
val apiTagRole = ResourceDocTag("Role")
val apiTagScope = ResourceDocTag("Scope")
val apiTagOwnerRequired = ResourceDocTag("OwnerViewRequired")
val apiTagCounterparty = ResourceDocTag("Counterparty")
val apiTagKyc = ResourceDocTag("KYC")
@ -32,20 +34,20 @@ object ApiTag {
val apiTagExperimental = ResourceDocTag("Experimental")
val apiTagPerson = ResourceDocTag("Person")
val apiTagCard = ResourceDocTag("Card")
val apiTagSandbox = ResourceDocTag("API-Sandbox")
val apiTagSandbox = ResourceDocTag("Sandbox")
val apiTagBranch = ResourceDocTag("Branch")
val apiTagATM = ResourceDocTag("ATM")
val apiTagProduct = ResourceDocTag("Product")
val apiTagOpenData = ResourceDocTag("Open-Data")
val apiTagConsumer = ResourceDocTag("API-Consumer")
val apiTagConsumer = ResourceDocTag("Consumer")
val apiTagSearchWarehouse = ResourceDocTag("Data-Warehouse")
val apiTagFx = ResourceDocTag("FX")
val apiTagMessage = ResourceDocTag("Customer-Message")
val apiTagMetric = ResourceDocTag("API-Metric")
val apiTagDocumentation = ResourceDocTag("API-Documentation")
val apiTagMetric = ResourceDocTag("Metric")
val apiTagDocumentation = ResourceDocTag("Documentation")
val apiTagBerlinGroup = ResourceDocTag("Berlin-Group")
val apiTagUKOpenBanking = ResourceDocTag("UKOpenBanking")
val apiTagApiBuilder = ResourceDocTag("API_Builder")
val apiTagApiBuilder = ResourceDocTag("API-Builder")
val apiTagAggregateMetrics = ResourceDocTag("Aggregate-Metrics")
val apiTagNewStyle = ResourceDocTag("New-Style")
val apiTagWebhook = ResourceDocTag("Webhook")

View File

@ -9,7 +9,6 @@ import code.util.Helper.MdcLoggable
import com.nimbusds.jose._
import com.nimbusds.jose.crypto.{MACSigner, RSAEncrypter}
import com.nimbusds.jwt.{EncryptedJWT, JWTClaimsSet}
import javax.crypto.Cipher
object CryptoSystem extends Enumeration {
@ -65,7 +64,22 @@ object CertificateUtil extends MdcLoggable {
keyPairGenerator.genKeyPair
}
def jwtWithHmacProtection(claimsSet: JWTClaimsSet) = {
def jwtWithHmacProtection(claimsSet: JWTClaimsSet): String = {
// Create HMAC signer
val signer: JWSSigner = new MACSigner(sharedSecret)
import com.nimbusds.jose.{JWSAlgorithm, JWSHeader}
import com.nimbusds.jwt.SignedJWT
val signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet)
// Apply the HMAC protection
signedJWT.sign(signer)
// Serialize to compact form, produces something like
// eyJhbGciOiJIUzI1NiJ9.SGVsbG8sIHdvcmxkIQ.onO9Ihudz3WkiauDO2Uhyuz0Y18UASXlSc1eS0NkWyA
val s: String = signedJWT.serialize()
// logger.info("jwtWithHmacProtection: " + s)
s
}
def jwtWithHmacProtection(claimsSet: JWTClaimsSet, sharedSecret: String): String = {
// Create HMAC signer
val signer: JWSSigner = new MACSigner(sharedSecret)
import com.nimbusds.jose.{JWSAlgorithm, JWSHeader}
@ -80,7 +94,16 @@ object CertificateUtil extends MdcLoggable {
s
}
def verifywtWithHmacProtection(jwt: String) = {
def verifywtWithHmacProtection(jwt: String): Boolean = {
import com.nimbusds.jose.crypto.MACVerifier
import com.nimbusds.jwt.SignedJWT
val signedJWT: SignedJWT = SignedJWT.parse(jwt)
// your-at-least-256-bit-secret
val verifier = new MACVerifier(sharedSecret)
signedJWT.verify(verifier)
}
def verifywtWithHmacProtection(jwt: String, sharedSecret: String): Boolean = {
import com.nimbusds.jose.crypto.MACVerifier
import com.nimbusds.jwt.SignedJWT
val signedJWT: SignedJWT = SignedJWT.parse(jwt)

View File

@ -51,6 +51,8 @@ object ErrorMessages {
val InvalidUrl = "OBP-10017: Incorrect URL Format. "
val TooManyRequests = "OBP-10018: Too Many Requests."
val InvalidBoolean = "OBP-10019: Invalid Boolean. Could not convert value to a boolean type."
val InvalidUri = "OBP-404: 404 Not Found. The server has not found anything matching the Request-URI.Check your URL and the headers. " +
"NOTE: when it is POST or PUT api, the Content-Type must be `application/json`. OBP only support the json format body."
// General Sort and Paging
val FilterSortDirectionError = "OBP-10023: obp_sort_direction parameter can only take two values: DESC or ASC!" // was OBP-20023
@ -131,6 +133,9 @@ object ErrorMessages {
val Oauth2ThereIsNoUrlOfJwkSet = "OBP-20203: There is no an URL of OAuth 2.0 server's JWK set, published at a well-known URL."
val Oauth2BadJWTException = "OBP-20204: Bad JWT error. "
val Oauth2ParseException = "OBP-20205: Parse error. "
val Oauth2BadJOSEException = "OBP-20206: Bad JSON Object Signing and Encryption (JOSE) exception. "
val Oauth2JOSEException = "OBP-20207: Javascript Object Signing and Encryption (JOSE) exception. "
val Oauth2CannotMatchIssuerAndJwksUriException = "OBP-20208: Cannot match the issuer and JWKS URI at this server instance. "
val InvalidAmount = "OBP-20054: Invalid amount. Please specify a valid value for amount."
val MissingQueryParams = "OBP-20055: These query parameters are missing: "
@ -236,7 +241,7 @@ object ErrorMessages {
val InvalidStrongPasswordFormat = "OBP-30207: Invalid Password Format. Your password should EITHER be at least 10 characters long and contain mixed numbers and both upper and lower case letters and at least one special character, OR be longer than 16 characters."
val AccountIdAlreadyExsits = "OBP-30208: Account_ID already exists at the Bank."
val AccountIdAlreadyExists = "OBP-30208: Account_ID already exists at the Bank."
val InsufficientAuthorisationToCreateBranch = "OBP-30209: Insufficient authorisation to Create Branch. You do not have the role CanCreateBranch." // was OBP-20019
@ -253,6 +258,12 @@ object ErrorMessages {
val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. "
val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. "
val AccountApplicationNotFound = "OBP-30311: AccountApplication not found by ACCOUNT_APPLICATION_ID. "
val ResourceUserNotFound = "OBP-30312: ResourceUser not found by USER_ID. "
val UserIdAndCustomerIdNotPresent = "OBP-30313: userId and customerId not present. "
val AccountApplicationAlreadyAccepted = "OBP-30314: AccountApplication has already been accepted. "
val UpdateAccountApplicationStatusError = "OBP-30315: AccountApplication Status can not be updated. "
val CreateAccountApplicationError = "OBP-30316: AccountApplication Status can not be created. "
// Branch related messages
val branchesNotFoundLicense = "OBP-32001: No branches available. License may not be set."
@ -303,6 +314,8 @@ object ErrorMessages {
val NoCallContext = "OBP-50012: Can not get the CallContext object here."
val UnspecifiedCbsError = "OBP-50013: The Core Banking System returned an unspecified error or response."
val RefreshUserError = "OBP-50014: Can not refresh User."
val InternalServerError = "OBP-50015: The server encountered an unexpected condition which prevented it from fulfilling the request."
// Connector Data Exceptions (OBP-502XX)
val ConnectorEmptyResponse = "OBP-50200: Connector cannot return the data we requested." // was OBP-30200

View File

@ -30,7 +30,7 @@ object ExampleValue {
val usernameExample = ConnectorField("felixsmith", s"The username the user uses to authenticate.")
glossaryItems += makeGlossaryItem("Adapter.username", usernameExample)
val correlationIdExample = ConnectorField("2ba9a7e4-6d02-40e3-a129-0b2bf89de92b", s"A UUID string generated by OBP-API that MUST uniquely identify the API call recieved by OBP-API. Used for debugging and logging purposes. It is returned in header to the caller.")
val correlationIdExample = ConnectorField("1flssoftxq0cr1nssr68u0mioj", s"A string generated by OBP-API that MUST uniquely identify the API call received by OBP-API. Used for debugging and logging purposes. It is returned in header to the caller.")
glossaryItems += makeGlossaryItem("API.correlation_id", correlationIdExample)
@ -77,6 +77,8 @@ object ExampleValue {
val accountTypeExample = ConnectorField("AC","A short code that represents the type of the account as provided by the bank.")
val balanceAmountExample = ConnectorField("50.89", "The balance on the account.")
val creditLimitAmountExample = ConnectorField("1000.00", "The credit limit on the accounts of a customer.")
val transactionAmountExample = ConnectorField("19.64", "A Transaction Amount.")

View File

@ -85,101 +85,205 @@ object Glossary {
glossaryItems += GlossaryItem(
title = "Cheat Sheet",
description =
s"""
|## A selection of links to get you started using the Open Bank Project API platform, applications and tools.
|
|[OBP API Installation](https://github.com/OpenBankProject/OBP-API/blob/develop/README.md)
|
|[OBP API Contributing](https://github.com/OpenBankProject/OBP-API/blob/develop/CONTRIBUTING.md)
|
|[Access Control](/glossary#API.Access-Control)
|
|[Versioning](https://github.com/OpenBankProject/OBP-API/wiki/API-Versioning)
|
|[Authentication](https://github.com/OpenBankProject/OBP-API/wiki/Authentication)
|
|[Interfaces](/glossary#API.Interfaces)
|
|[Endpoints](https://apiexplorersandbox.openbankproject.com)
|
|[Glossary](/glossary)
|
|[Access Control](/glossary#API.Access-Control)
|
|[OBP Kafka](/glossary#Adapter.Kafka.Intro)
|
|[OBP Akka](/glossary#Adapter.Akka.Intro)
|
|[API Explorer](https://github.com/OpenBankProject/API-Explorer/blob/develop/README.md)
|
|[API Manager](https://github.com/OpenBankProject/API-Manager/blob/master/README.md)
|
|[API Tester](https://github.com/OpenBankProject/API-Tester/blob/master/README.md)
|
|
""")
glossaryItems += GlossaryItem(
title = "Adapter.Akka.Intro",
description =
s"""
|## Use Akka as an interface between OBP and your Core Banking System (CBS).
|
|For an introduction to Akka see [here](https://akka.io/)
|
|The OBP Akka interface allows integrators to write Java or Scala Adapters (any JVM language with Akka support)
|respond to requests for data and services from OBP.
|
|For the message definitions see [here](/message-docs?connector=akka_vDec2018)
|
|This glossary item is Work In Progress.
|
""")
glossaryItems += GlossaryItem(
title = "Adapter.Kafka.Intro",
description =
s"""
|## Brief introduction to using Kafka as the interface layer between OBP and your Core Banking System (CBS).
|### Prerequesites
|## Use Kafka as an interface between OBP and your Core Banking System (CBS).
|
|
|For an introduction to Kafka see [here](https://kafka.apache.org/)
|
|### Installation Prerequisites
|
|
|* We assume you have OBP-API running and it is connected to a working Kafka installation.
| You can check OBP -> Kafka connectivity using the <a href="/#vv3_1_0-getObpApiLoopback">"loopback" endpoint</a>.
|* You have OBP-API running and it is connected to a Kafka installation.
| You can check OBP -> Kafka connectivity using the <a href="/#vv3_1_0-getObpApiLoopback">"loopback" endpoint</a>.
|
|* Ideally you have API Explorer running (the application serving this page) but its not necessary - you could use any other REST client.
|* You might want to also run API Manager as it makes it easier to grant yourself roles, but its not necessary - you could use the API Explorer / any REST client instead.
|
|### Create a Customer User and an Admin User
|
|* We assume you have API Explorer running (the application serving this page) but its not necessary - (you could use a REST client)
|* You might want to also run API Manager as it makes it easier to grant yourself roles, but its not nessessary (you could use a REST client / API Explorer instead).
|* You should register a User that you want to use to call the API. Let's call this user Jane.
|* You will need another user that will have the roles required for the following steps. Let's call this user CarolaAdmin.
|* Use <a href="/index#vv3_1_0-createUserAuthContext">Create Auth Context</a> to add a token to the User who while request accounts.
|This token which could be a CUSTOMER_NUMBER is sent inside the AuthInfo object to Kafka
|* OR Use Create Customer and Create User Customer Link (note that Create Auth Context is preferred)
|
|Then its time to configure or program your Adapter to consume, and respond to, the messages OBP will send to Kafka.
|* Register a User who will use the API as a Customer.
|* Register another User that will use the API as an Admin. The Admin user will need some Roles. See [here](/index#vv2_0_0-addEntitlement). You can bootstrap an Admin user by editing the Props file. See the README for that.
|
| We suggest they are implemented in the following order:
|### Add some authentication context to the Customer User
|
|* As the Admin User, use the [Create Auth Context](/index#vv3_1_0-createUserAuthContext) endpoint to add one or more attributes to the Customer User.
|For instance you could add the name/value pair CUSTOMER_NUMBER/889763 and this will be sent to the Adapter / CBS inside the AuthInfo object.
|
|
|Now you should be able to use the [Get Auth Contexts](/index#vv3_1_0-getUserAuthContexts) endpoint to see the data you added.
|
|### Write or Build an Adapter to respond to the following messages.
|
| When getting started, we suggest that you implement the messages in the following order:
|
|1) Core (Prerequisites) - Get Adapter, Get Banks, Get Bank)
|
|* ${messageDocLink("obp.get.AdapterInfo")}
|* ${messageDocLink("obp.get.Banks")}
|* ${messageDocLink("obp.get.Bank")}
|
|2) Get Accounts
|* ${messageDocLink("obp.get.AdapterInfo")}
|
|* ${messageDocLink("obp.get.CustomersByUserIdBox")}
|* ${messageDocLink("obp.get.coreBankAccounts")}
|* ${messageDocLink("obp.check.BankAccountExists")}
|* ${messageDocLink("obp.get.Accounts")}
|* ${messageDocLink("obp.get.Account")}
|
|3) Get Transactions
|Now you should be able to use the [Adapter Info](/index#vv3_0_0-getAdapter) endpoint
|
|* ${messageDocLink("obp.get.Banks")}
|
|Now you should be able to use the [Get Banks](/index#vv3_0_0-getBanks) endpoint
|
|* ${messageDocLink("obp.get.Bank")}
|
|Now you should be able to use the [Get Bank](/index#vv3_0_0-bankById) endpoint
|
|
|2) Customers for logged in User
|
|* ${messageDocLink("obp.get.CustomersByUserIdBox")}
|
|Now you should be able to use the [Get Customers](/index#vv3_0_0-getCustomersForUser) endpoint.
|
|
|3) Get Accounts
|
|* ${messageDocLink("obp.check.BankAccountExists")}
|* ${messageDocLink("obp.get.coreBankAccounts")}
|* ${messageDocLink("obp.get.Accounts")}
|
| The above messages should enable at least the following endpoints:
|
|* [Get Accounts Held](http://localhost:8082/index#vv3_0_0-getAccountsHeld)
|* [Get Account IDs](/index#vv3_0_0-getPrivateAccountIdsbyBankId)
|* [Get Accounts - Minimal](index#vv3_0_0-privateAccountsAtOneBank)
|
|4) Get Account
|
|* ${messageDocLink("obp.get.Account")}
|
| The above message should enable at least the following endpoints:
|
|* [Get Account by Id - Core](/index#vv3_0_0-getCoreAccountById)
|* [Get Account by Id - Full](/index#vv3_0_0-getPrivateAccountById)
|
|5) Get Transactions
|
|* ${messageDocLink("obp.get.Transactions")}
|* ${messageDocLink("obp.get.Transaction")}
|
|4) Manage Counterparties
|6) Manage Counterparties
|
|* ${messageDocLink("obp.get.counterparties")}
|* ${messageDocLink("obp.get.CounterpartyByCounterpartyId")}
|* ${messageDocLink("obp.create.Counterparty")}
|
|5) Get Transaction Request Types
|7) Get Transaction Request Types
|
|* This is configured using OBP Props - No messages required
|
|6) Get Challenge Threshold (CBS)
|8) Get Challenge Threshold (CBS)
|
|* ${messageDocLink("obp.get.getChallengeThreshold")}
|
|7) Make Payment (used by Create Transaction Request)
|9) Make Payment (used by Create Transaction Request)
|
|* ${messageDocLink("obp.get.makePaymentv210")}
|* This also requires 8,9,10 for high value payments.
|
|8) Get Transaction Requests.
|10) Get Transaction Requests.
|
|* ${messageDocLink("obp.get.transactionRequests210")}
|
|9) Generate Security Challenges (CBS)
|11) Generate Security Challenges (CBS)
|
|* ${messageDocLink("obp.create.Challenge")}
|
|10) Answer Security Challenges (Validate)
|12) Answer Security Challenges (Validate)
|
|* Optional / Internal OBP (No additional messages required)
|
|11) Manage Counterparty Metadata
|13) Manage Counterparty Metadata
|
|* Internal OBP (No additional messages required)
|
|12) Get Entitlements
|14) Get Entitlements
|
|* Internal OBP (No additional messages required)
|
|13) Manage Roles
|15) Manage Roles
|
|* Internal OBP (No additional messages required)
|
|14) Manage Entitlements
|16) Manage Entitlements
|
|* Internal OBP (No additional messages required)
|
|15) Manage Views
|17) Manage Views
|
|* Internal OBP (No additional messages required)
|
|16) Manage Transaction Metadata
|18) Manage Transaction Metadata
|
|* Internal OBP (No additional messages required)
|
@ -224,13 +328,65 @@ object Glossary {
title = "API.Interfaces",
description =
s"""
|<img width="468" alt="authinfo_annotated_1" src="https://user-images.githubusercontent.com/485218/48845997-413e0780-ed9e-11e8-86c5-e5ce510c140c.png"></img>
|<img width="468" alt="OBP Interfaces Image" src="https://user-images.githubusercontent.com/485218/49711990-9ef99d00-fc42-11e8-8cb4-cc68bab74703.png"></img>
|
|
|
|"""
)
glossaryItems += GlossaryItem(
title = "API.Timeouts",
description =
s"""
|<img width="1403" alt="OBP Timeouts Image" src="https://user-images.githubusercontent.com/29032407/50471858-b52f8900-09b6-11e9-9888-454e6d41907c.png"></img>
|
|
|
|"""
)
glossaryItems += GlossaryItem(
title = "API.Access Control",
description =
s"""
|
|Access Control is achieved via the following mechanisms in OBP:
|
|* APIs are enabled in Props. See the README.md
|
|* Consumers (Apps) are granted access to Roles and Views via Scopes (WIP)
|
|See [here](/index#group-Scope) for related endpoints and documentation.
|
|* Users are granted access to System or Bank Roles via Entitlements.
|
|See [here](/index#group-Role) for related endpoints and documentation.
|
|Users may request Entitlement Requests [here](/index#vv3_0_0-addEntitlementRequest)
|
|Entitlements and Entitlement Requests can be managed in the OBP API Manager.
|
|* Users are granted access to Customer Accounts, Transactions and Payments via Views.
|
|See [here](/index#group-View) for related endpoints and documentation.
|
|User Views can be managed via the OBP Sofit Consent App.
|
|
|<img width="468" alt="OBP Access Control Image" src="https://user-images.githubusercontent.com/485218/49863122-e6795800-fdff-11e8-9b05-bba99e2c72da.png"></img>
|
|
|
|"""
)
glossaryItems += GlossaryItem(
title =
@ -357,7 +513,7 @@ object Glossary {
title = "User.provider_id",
description =
"""
|The id of the user given by the authenticaiton provider.
|The id of the user given by the authentication provider.
""")
glossaryItems += GlossaryItem(
@ -1173,6 +1329,104 @@ object Glossary {
""")
glossaryItems += GlossaryItem(
title = "OAuth 2 with Google",
description =
s"""
|
|$oauth2EnabledMessage
|
|## OpenID Connect with Google
|
|### Introduction
|Google's OAuth 2.0 APIs can be used for both authentication and authorization. This document describes our OAuth 2.0 implementation for authentication, which conforms to the OpenID Connect specification, and is OpenID Certified.
|For complete documentation please refer to the official doc's page: [OpenID Connect](https://developers.google.com/identity/protocols/OpenIDConnect)
|
|### Obtain OAuth 2.0 credentials
|Please refer to the official doc's page: [OpenID Connect](https://developers.google.com/identity/protocols/OpenIDConnect)
|
|### An ID token's payload
|
|
| {
| "iss": "https://accounts.google.com",
| "azp": "407408718192.apps.googleusercontent.com",
| "aud": "407408718192.apps.googleusercontent.com",
| "sub": "113966854245780892959",
| "email": "marko.milic.srbija@gmail.com",
| "email_verified": true,
| "at_hash": "nGKRToKNnVA28H6MhwXBxw",
| "name": "Marko Milić",
| "picture": "https://lh5.googleusercontent.com/-Xd44hnJ6TDo/AAAAAAAAAAI/AAAAAAAAAAA/AKxrwcadwzhm4N4tWk5E8Avxi-ZK6ks4qg/s96-c/photo.jpg",
| "given_name": "Marko",
| "family_name": "Milić",
| "locale": "en",
| "iat": 1547705691,
| "exp": 1547709291
| }
|
|
|### Try a REST call using the authorization's header
| Using your favorite http client:
|
| GET /obp/v3.0.0/users/current
|
|Body
|
|Leave Empty!
|
|Headers:
|
|
| Authorization: Bearer ID_TOKEN
|
|
|Here is it all together:
|
|
|
| GET /obp/v3.0.0/users/current HTTP/1.1
| Host: $getServerUrl
| Authorization: Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjA4ZDMyNDVjNjJmODZiNjM2MmFmY2JiZmZlMWQwNjk4MjZkZDFkYzEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJlbWFpbCI6Im1hcmtvLm1pbGljLnNyYmlqYUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6IkFvYVNGQTlVTTdCSGg3YWZYNGp2TmciLCJuYW1lIjoiTWFya28gTWlsacSHIiwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tWGQ0NGhuSjZURG8vQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQUt4cndjYWR3emhtNE40dFdrNUU4QXZ4aS1aSzZrczRxZy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiTWFya28iLCJmYW1pbHlfbmFtZSI6Ik1pbGnEhyIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTQ3NzExMTE1LCJleHAiOjE1NDc3MTQ3MTV9.MKsyecCSKS4Y0C8R4JP0J0d2Oa-xahvMAbtfFrGHncTm8xBgeaNb50XSJn20ak1YyA8hZiRP2M3el0f4eIVQZsMMa22MrwaiL8pLb1zGfawDLPb1RvOmoCWTDJGc_s1qQMlyc21Wenr9rjuu1bQCerGTYM6M0Aq-Uu_GT0lCEjz5WVDI5xDUf4Mhdi8HYq7UQ1kGz1gQFiBm5nI3_xtYm75EfXFeDg3TejaMmy36NpgtwN_vwpHByoHE5BoTl2J55rJ2creZZ7CmtZttm-9HsT6v1vxT8zi0RXObFrZSk-LgfF0tJQcGZ5LXQZL0yMKXPQVFIMCg8J0Gg7l_QACkCA
| Cache-Control: no-cache
|
|
|
|CURL example:
|
|
| curl -X GET
| $getServerUrl/obp/v3.0.0/users/current
| -H 'Authorization: Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjA4ZDMyNDVjNjJmODZiNjM2MmFmY2JiZmZlMWQwNjk4MjZkZDFkYzEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJlbWFpbCI6Im1hcmtvLm1pbGljLnNyYmlqYUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6IkFvYVNGQTlVTTdCSGg3YWZYNGp2TmciLCJuYW1lIjoiTWFya28gTWlsacSHIiwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tWGQ0NGhuSjZURG8vQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQUt4cndjYWR3emhtNE40dFdrNUU4QXZ4aS1aSzZrczRxZy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiTWFya28iLCJmYW1pbHlfbmFtZSI6Ik1pbGnEhyIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTQ3NzExMTE1LCJleHAiOjE1NDc3MTQ3MTV9.MKsyecCSKS4Y0C8R4JP0J0d2Oa-xahvMAbtfFrGHncTm8xBgeaNb50XSJn20ak1YyA8hZiRP2M3el0f4eIVQZsMMa22MrwaiL8pLb1zGfawDLPb1RvOmoCWTDJGc_s1qQMlyc21Wenr9rjuu1bQCerGTYM6M0Aq-Uu_GT0lCEjz5WVDI5xDUf4Mhdi8HYq7UQ1kGz1gQFiBm5nI3_xtYm75EfXFeDg3TejaMmy36NpgtwN_vwpHByoHE5BoTl2J55rJ2creZZ7CmtZttm-9HsT6v1vxT8zi0RXObFrZSk-LgfF0tJQcGZ5LXQZL0yMKXPQVFIMCg8J0Gg7l_QACkCA'
| -H 'Cache-Control: no-cache'
| -H 'Postman-Token: aa812d04-eddd-4752-adb7-4d56b3a98f36'
|
|
|
|And we get the response:
|
|
| {
| "user_id": "6d411bce-50c1-4eb8-b8b0-3953e4211773",
| "email": "marko.milic.srbija@gmail.com",
| "provider_id": "113966854245780892959",
| "provider": "https://accounts.google.com",
| "username": "Marko Milić",
| "entitlements": {
| "list": []
| }
| }
|
|
|""")
val gatewayLoginEnabledMessage : String = if (APIUtil.getPropsAsBoolValue("allow_gateway_login", false))
{"Note: Gateway Login is enabled."} else {"Note: *Gateway Login is NOT enabled on this instance!*"}

View File

@ -1,17 +1,19 @@
package code.api.util
import java.net.URL
import java.text.ParseException
import com.auth0.jwt.JWT
import com.auth0.jwt.interfaces.Claim
import com.nimbusds.jose.JWSAlgorithm
import com.nimbusds.jose.crypto.{MACVerifier, RSASSAVerifier}
import com.nimbusds.jose.jwk.source.{JWKSource, RemoteJWKSet}
import com.nimbusds.jose.proc.{JWSVerificationKeySelector, SecurityContext}
import com.nimbusds.jwt.{JWTClaimsSet, SignedJWT}
import com.nimbusds.jose.util.DefaultResourceRetriever
import com.nimbusds.jwt.proc.{BadJWTException, DefaultJWTProcessor}
import com.nimbusds.jwt.{JWTClaimsSet, SignedJWT}
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet
import net.liftweb.common.{Box, Empty, Failure, Full}
import net.liftweb.util.Props
import java.text.ParseException
object JwtUtil {
@ -64,9 +66,8 @@ object JwtUtil {
*
* @return True or False
*/
def verifyHmacSignedJwt(jwtToken: String): Boolean = {
def verifyHmacSignedJwt(jwtToken: String, sharedSecret: String): Boolean = {
val signedJWT = SignedJWT.parse(jwtToken)
val sharedSecret = APIUtil.getPropsValue("oauth2.token_secret", "")
val verifier = new MACVerifier(sharedSecret)
signedJWT.verify(verifier)
}
@ -74,7 +75,7 @@ object JwtUtil {
/**
* Get the value of the "sub" claim, or None if it's not available.
*
* @return the Subject value or None.
* @return the Subject's value or None.
*/
def getSubject(jwtToken: String): Option[String] = {
val jwtDecoded = JWT.decode(jwtToken)
@ -83,9 +84,47 @@ object JwtUtil {
case value => Some(value)
}
}
/**
* The Issuer Identifier for the Issuer of the response.
* Get the value of the "iss" claim, or None if it's not available.
*
* @return the Issuer's value or None.
*/
def getIssuer(jwtToken: String): Option[String] = {
val jwtDecoded = JWT.decode(jwtToken)
jwtDecoded.getIssuer() match {
case null => None
case value => Some(value)
}
}
/**
* The Audience Identifier for the Issuer of the response.
* Get the value of the "aud" claim.
*
* @return the Issuer's value. In case if it's not available the value is empty list.
*/
def getAudience(jwtToken: String): List[String] = {
import scala.collection.JavaConverters._
val jwtDecoded = JWT.decode(jwtToken)
jwtDecoded.getAudience() match {
case null => Nil
case value => value.asScala.toList
}
}
/**
* Helper function which validating bearer JWT access tokens
* This fuction gets an arbitrary claim
* @param name The name of the claim we want to get
* @param jwtToken JSON Web Token (JWT) as a String value
* @return The claim we requested
*/
def getClaim(name: String, jwtToken: String): Claim = {
val jwtDecoded = JWT.decode(jwtToken)
jwtDecoded.getClaim(name)
}
/**
* This function validates Access Token
* @param accessToken The access token to validate, typically submitted with a HTTP header like
* Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InMxIn0.eyJzY3A...
* @param remoteJWKSetUrl The URL of OAuth 2.0 server's JWK set, published at a well-known URL
@ -124,17 +163,70 @@ object JwtUtil {
}
}
/**
* This function validates ID Token
* @param idToken The access token to validate, typically submitted with a HTTP header like
* Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InMxIn0.eyJzY3A...
* @param remoteJWKSetUrl The URL of OAuth 2.0 server's JWK set, published at a well-known URL
* @return The boxed token claims set or Failure
*/
def validateIdToken(idToken: String, remoteJWKSetUrl: String): Box[IDTokenClaimsSet] = {
import java.net._
import com.nimbusds.jose._
import com.nimbusds.oauth2.sdk.id._
import com.nimbusds.openid.connect.sdk.validators._
val resourceRetriever = new DefaultResourceRetriever(1000, 1000, 50 * 1024)
// The required parameters
val iss: Issuer = new Issuer(getIssuer(idToken).getOrElse(""))
val azp = getClaim("azp", idToken).asString()
val clientID: ClientID = new ClientID(azp)
val jwsAlg: JWSAlgorithm = JWSAlgorithm.RS256
//val jwkSetURL: URL = new URL("https://www.googleapis.com/oauth2/v3/certs")
val jwkSetURL: URL = new URL(remoteJWKSetUrl)
// Create validator for signed ID tokens
val validator: IDTokenValidator = new IDTokenValidator(iss, clientID, jwsAlg, jwkSetURL, resourceRetriever)
import com.nimbusds.jose.JOSEException
import com.nimbusds.jose.proc.BadJOSEException
import com.nimbusds.jwt.{JWT, JWTParser}
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet
// Parse the ID token// Parse the ID token
val idTokenAsJWT: JWT = JWTParser.parse(idToken)
// Set the expected nonce, leave null if none
val expectedNonce = null // new Nonce("xyz...") or null
try {
val claims: IDTokenClaimsSet = validator.validate(idTokenAsJWT, expectedNonce)
Full(claims)
} catch {
case e: BadJOSEException =>
// Invalid signature or claims (iss, aud, exp...)
Failure(ErrorMessages.Oauth2BadJOSEException + e.getMessage, Full(e), Empty)
case e: JOSEException =>
// Internal processing exception
Failure(ErrorMessages.Oauth2JOSEException + e.getMessage, Full(e), Empty)
}
}
def main(args: Array[String]): Unit = {
// val jwtToken = "eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJhZG1pbiIsImF6cCI6ImNsaWVudCIsImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJleHAiOjE1MTk1MDMxODAsImlhdCI6MTUxOTQ5OTU4MCwianRpIjoiMmFmZjNhNGMtZjY5Zi00ZWM1LWE2MzEtYWUzMGYyYzQ4MjZiIn0.NwlK2EJKutaybB4YyEhuwb231ZNkD-BEwhScadcWWn8PFftjVyjqjD5_BwSiWHHa_QaESNPdZugAnF4I2DxtXmpir_x2fB2ch888AzXw6CgTT482I16m1jpL-2iSlQk1D-ZW6fJ2Qemdi3x2V13Xgt9PBvk5CsUukJ8SSqTPbSNNER9Nq2dlS-qQfg61TzhPkuuXDlmCQ3b8QHgUf6UnCfee1jRaohHQoCvJJJubmUI3dY0Df1ynTodTTZm4J1TV6Wp6ZhsPkQVmdBAUsE5kIFqADaE179lldh86-97bVHGU5a4aTYRRKoTPDltt1NvY5XJrjLCgZH8AEW7mOHz9mw"
val jwtToken = "eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJtYXJrby5taWxpYyIsImF6cCI6ImNsaWVudCIsImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJleHAiOjE1MTk3MTc2MDUsImlhdCI6MTUxOTcxNDAwNSwianRpIjoiY2RiNThmNTctZTI2OC00MzZhLWIzMDQtNWE0MWFiYTg0NDFhIn0.XiZKY8A_mXZz6zjCgtXaj0bHI5klmQGnEQcX_b9lBlhfh6IruUwiHuYW0DHXDpKHdKA3Uuqcubj68aT8r5FGyrEGRy4AmzHbzCcwly-MYIElAK4trjSwUJh9VmGwDdr1OFtWC5HrTfsTGfiLQrhNjBGePCy2bGy0pG7pjBNQ3TVOkiAFUVYnCJOiFGLdWcHvEHnPYoYOdvRBLa072qDFbNFiWXqfKcdXdYGXZD5SGMMlA6J6l3NKKiy4t53yE3LjHs5pIclG5OdSV3uB8wGTTACN44CMVUFpWaL6_7_Zlzr-swq_jXYuxHesWGoCWaZKzlbtHsOqpvolgQJlTgdAgA"
val jwtToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhhYWQ2NmJkZWZjMWI0M2Q4ZGIyN2U2NWUyZTJlZjMwMTg3OWQzZTgiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJhdF9oYXNoIjoiWGlpckZ1cnJ2X0ZxN3RHd25rLWt1QSIsIm5hbWUiOiJNYXJrbyBNaWxpxIciLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDUuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1YZDQ0aG5KNlREby9BQUFBQUFBQUFBSS9BQUFBQUFBQUFBQS9BS3hyd2NhZHd6aG00TjR0V2s1RThBdnhpLVpLNmtzNHFnL3M5Ni1jL3Bob3RvLmpwZyIsImdpdmVuX25hbWUiOiJNYXJrbyIsImZhbWlseV9uYW1lIjoiTWlsacSHIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE1NDczMTE3NjAsImV4cCI6MTU0NzMxNTM2MH0.UyOmM0rsO0-G_ibDH3DFogS94GcsNd9GtYVw7j3vSMjO1rZdIraV-N2HUtQN3yHopwdf35A2FEJaag6X8dbvEkJC7_GAynyLIpodoaHNtaLbww6XQSYuQYyF27aPMpROoGZUYkMpB_82LF3PbD4ecDPC2IA5oSyDF4Eya4yn-MzxYmXS7usVWvanREg8iNQSxpu7zZqj4UwhvSIv7wH0vskr_M-PnefQzNTrdUx74i-v9lVqC4E_bF5jWeDGO8k5dqWqg55QuZdyJdSh89KNiIjJXGZDWUBzGfsbetWRnObIgX264fuOW4SpRglUc8fzv41Sc7SSqjqRAFm05t60kg"
println("Header: " + getHeader(jwtToken))
println("Payload: " + getPayload(jwtToken))
println("Subject: " + getSubject(jwtToken))
println("Signature :" + getSignature(jwtToken))
println("Verify JWT :" + verifyRsaSignedJwt(jwtToken))
println("Signature: " + getSignature(jwtToken))
println("Verify JWT: " + verifyRsaSignedJwt(jwtToken))
val idToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhhYWQ2NmJkZWZjMWI0M2Q4ZGIyN2U2NWUyZTJlZjMwMTg3OWQzZTgiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM5NjY4NTQyNDU3ODA4OTI5NTkiLCJhdF9oYXNoIjoiWVVuME9EYlVsUU4xQ1VUOThSVmE3USIsIm5hbWUiOiJNYXJrbyBNaWxpxIciLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDUuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1YZDQ0aG5KNlREby9BQUFBQUFBQUFBSS9BQUFBQUFBQUFBQS9BS3hyd2NhZHd6aG00TjR0V2s1RThBdnhpLVpLNmtzNHFnL3M5Ni1jL3Bob3RvLmpwZyIsImdpdmVuX25hbWUiOiJNYXJrbyIsImZhbWlseV9uYW1lIjoiTWlsacSHIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE1NDc0NTQ3MTIsImV4cCI6MTU0NzQ1ODMxMn0.GImeYoPuOgitxpS59XvEd93nxRYKbWl9vHvuMIXYJWFQ5bF_LcnX_PdXRA3w-cBrAZZ3FCAtY0nrE8f7pb6-oQnqpJXYl6PwCe_oZV5rUzMnWUyWauk752_Et-hSxCypAyf7zvW3xcunQUdeKLt_b5dIIs80d8vDpnSlR4SkXx9iduOQ84ktvHMgwIb7ymws6LenstJH864TMvmUNokFgVGOcVeJRKKiGmcoIhIYdh9j1z4J0_gCPs-UsJhJTdmVQgtNQFqMUt8KPEYvFd0gI3Cdvd9gQM5cq9OSUs3D9sI0DLEhBCoEHanBinUrII8B7JE2HkPTEMdM2ZN-2Ecq5A"
println("validateIdToken: " + validateIdToken(idToken = idToken, remoteJWKSetUrl = "https://www.googleapis.com/oauth2/v3/certs").map("Logged in user: " + _.getSubject))
}

View File

@ -1,6 +1,7 @@
package code.api.util
import code.api.util.APIUtil.getPropsAsBoolValue
import code.consumer.Consumers
import code.customer.Customer
object Migration {
@ -17,6 +18,10 @@ object Migration {
Customer.customerProvider.vend.populateMissingUUIDs()
}
def generateAndPopulateMissingConsumersUUIDs(): Boolean = executeScript {
Consumers.consumers.vend.populateMissingUUIDs()
}
}
}

View File

@ -1,5 +1,6 @@
package code.api.util
import code.accountapplication.AccountApplication
import code.api.APIFailureNewStyle
import code.api.util.APIUtil.{OBPReturnType, createHttpParamsByUrlFuture, createQueriesByHttpParamsFuture, fullBoxOrException, unboxFull, unboxFullOrFail}
import code.api.util.ErrorMessages._
@ -13,7 +14,7 @@ import code.api.v3_1_0.OBPAPI3_1_0.Implementations3_1_0
import code.atms.Atms
import code.atms.Atms.AtmId
import code.bankconnectors.vMar2017.InboundAdapterInfoInternal
import code.bankconnectors.{Connector, OBPQueryParam, ObpApiLoopback}
import code.bankconnectors.{Connector, ObpApiLoopback}
import code.branches.Branches
import code.branches.Branches.BranchId
import code.consumer.Consumers
@ -32,9 +33,10 @@ import code.util.Helper
import code.views.Views
import code.webhook.AccountWebhook
import com.github.dwickern.macros.NameOf.nameOf
import net.liftweb.common.Box
import net.liftweb.common.{Box, Full}
import net.liftweb.http.provider.HTTPParam
import net.liftweb.util.Helpers.tryo
import org.apache.commons.lang3.StringUtils
import scala.collection.immutable.List
import scala.concurrent.Future
@ -43,6 +45,10 @@ object NewStyle {
lazy val endpoints: List[(String, String)] = List(
(nameOf(Implementations1_4_0.getTransactionRequestTypes), ApiVersion.v1_4_0.toString),
(nameOf(Implementations2_0_0.getAllEntitlements), ApiVersion.v2_0_0.toString),
(nameOf(Implementations2_0_0.publicAccountsAtOneBank), ApiVersion.v2_0_0.toString),
(nameOf(Implementations2_0_0.privateAccountsAtOneBank), ApiVersion.v2_0_0.toString),
(nameOf(Implementations2_0_0.corePrivateAccountsAtOneBank), ApiVersion.v2_0_0.toString),
(nameOf(Implementations2_0_0.getPrivateAccountsAtOneBank), ApiVersion.v2_0_0.toString),
(nameOf(Implementations2_1_0.getRoles), ApiVersion.v3_1_0.toString),
(nameOf(Implementations2_2_0.config), ApiVersion.v2_2_0.toString),
(nameOf(Implementations2_2_0.getViewsForBankAccount), ApiVersion.v2_2_0.toString),
@ -131,7 +137,11 @@ object NewStyle {
(nameOf(Implementations3_1_0.createProductAttribute), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.getProductAttribute), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.updateProductAttribute), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.deleteProductAttribute), ApiVersion.v3_1_0.toString)
(nameOf(Implementations3_1_0.deleteProductAttribute), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.createAccountApplication), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.getAccountApplications), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.getAccountApplication), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.updateAccountApplicationStatus), ApiVersion.v3_1_0.toString)
)
object HttpCode {
@ -187,10 +197,9 @@ object NewStyle {
}
def checkBankAccountExists(bankId : BankId, accountId : AccountId, callContext: Option[CallContext]) : OBPReturnType[BankAccount] = {
Future { Connector.connector.vend.checkBankAccountExists(bankId, accountId, callContext) } map {
Connector.connector.vend.checkBankAccountExistsFuture(bankId, accountId, callContext) } map {
unboxFullOrFail(_, callContext, s"$BankAccountNotFound Current BankId is $bankId and Current AccountId is $accountId", 400)
}
}
def moderatedBankAccount(account: BankAccount, view: View, user: Box[User]) = Future {
account.moderatedBankAccount(view, user)
@ -291,11 +300,9 @@ object NewStyle {
}
def getAdapterInfo(callContext: Option[CallContext]): OBPReturnType[InboundAdapterInfoInternal] = {
Future {
Connector.connector.vend.getAdapterInfo(callContext)
} map {
unboxFullOrFail(_, callContext, ConnectorEmptyResponse, 400)
}
Connector.connector.vend.getAdapterInfoFuture(callContext) map {
unboxFullOrFail(_, callContext, ConnectorEmptyResponse, 400)
}
}
def getEntitlementsByUserId(userId: String, callContext: Option[CallContext]): Future[List[Entitlement]] = {
@ -305,9 +312,9 @@ object NewStyle {
}
def getCounterparties(bankId : BankId, accountId : AccountId, viewId : ViewId, callContext: Option[CallContext]): OBPReturnType[List[CounterpartyTrait]] = {
Future(Connector.connector.vend.getCounterparties(bankId,accountId,viewId, callContext)) map {
x => fullBoxOrException(x ~> APIFailureNewStyle(ConnectorEmptyResponse, 400, callContext.map(_.toLight)))
} map { unboxFull(_) }
Connector.connector.vend.getCounterpartiesFuture(bankId,accountId,viewId, callContext) map { i=>
(unboxFullOrFail(i._1, callContext, ConnectorEmptyResponse, 400), i._2)
}
}
def getMetadata(bankId : BankId, accountId : AccountId, counterpartyId : String, callContext: Option[CallContext]): Future[CounterpartyMetadata] = {
@ -572,7 +579,80 @@ object NewStyle {
i => (unboxFullOrFail(i._1, callContext, ConnectorEmptyResponse, 400), i._2)
}
}
def createAccountApplication(
productCode: ProductCode,
userId: Option[String],
customerId: Option[String],
callContext: Option[CallContext]
): OBPReturnType[AccountApplication] =
Connector.connector.vend.createAccountApplication(productCode, userId, customerId, callContext) map {
i => (unboxFullOrFail(i._1, callContext, CreateAccountApplicationError, 400), i._2)
}
def getAllAccountApplication(callContext: Option[CallContext]): OBPReturnType[List[AccountApplication]] =
Connector.connector.vend.getAllAccountApplication(callContext) map {
i => (unboxFullOrFail(i._1, callContext, UnknownError, 400), i._2)
}
def getAccountApplicationById(accountApplicationId: String, callContext: Option[CallContext]): OBPReturnType[AccountApplication] =
Connector.connector.vend.getAccountApplicationById(accountApplicationId, callContext) map {
i => (unboxFullOrFail(i._1, callContext, s"$AccountApplicationNotFound Current Account-Application-Id($accountApplicationId)", 400), i._2)
}
def updateAccountApplicationStatus(accountApplicationId:String, status: String, callContext: Option[CallContext]): OBPReturnType[AccountApplication] =
Connector.connector.vend.updateAccountApplicationStatus(accountApplicationId, status, callContext) map {
i => (unboxFullOrFail(i._1, callContext, s"$UpdateAccountApplicationStatusError Current Account-Application-Id($accountApplicationId)", 400), i._2)
}
def findUsers(userIds: List[String], callContext: Option[CallContext]): OBPReturnType[List[User]] = Future {
val userList = userIds.filterNot(StringUtils.isBlank).distinct.map(User.findByUserId).collect {
case Full(user) => user
}
(userList, callContext)
}
def createSandboxBankAccount(
bankId: BankId,
accountId: AccountId,
accountType: String,
accountLabel: String,
currency: String,
initialBalance: BigDecimal,
accountHolderName: String,
branchId: String,
accountRoutingScheme: String,
accountRoutingAddress: String,
callContext: Option[CallContext]
): OBPReturnType[BankAccount] = Future {
Connector.connector.vend.createSandboxBankAccount(
bankId: BankId,
accountId: AccountId,
accountType: String,
accountLabel: String,
currency: String,
initialBalance: BigDecimal,
accountHolderName: String,
branchId: String,
accountRoutingScheme: String,
accountRoutingAddress: String
)} map {
i => (unboxFullOrFail(i, callContext, UnknownError, 400), callContext)
}
def findCustomers(customerIds: List[String], callContext: Option[CallContext]): OBPReturnType[List[Customer]] = {
val customerList = customerIds.filterNot(StringUtils.isBlank).distinct
.map(Consumers.consumers.vend.getConsumerByConsumerIdFuture)
Future.foldLeft(customerList)(List.empty[Customer]) { (prev, next) =>
next match {
case Full(customer:Customer) => customer :: prev
case _ => prev
}
} map {
(_, callContext)
}
}
}
}

View File

@ -0,0 +1,40 @@
package code.api.util
import java.util.Date
import scala.collection.immutable.List
class OBPQueryParam
trait OBPOrder { def orderValue : Int }
object OBPOrder {
def apply(s: Option[String]): OBPOrder = s match {
case Some("asc") => OBPAscending
case Some("ASC")=> OBPAscending
case _ => OBPDescending
}
}
object OBPAscending extends OBPOrder { def orderValue = 1 }
object OBPDescending extends OBPOrder { def orderValue = -1}
case class OBPLimit(value: Int) extends OBPQueryParam
case class OBPOffset(value: Int) extends OBPQueryParam
case class OBPFromDate(value: Date) extends OBPQueryParam
case class OBPToDate(value: Date) extends OBPQueryParam
case class OBPOrdering(field: Option[String], order: OBPOrder) extends OBPQueryParam
case class OBPConsumerId(value: String) extends OBPQueryParam
case class OBPUserId(value: String) extends OBPQueryParam
case class OBPBankId(value: String) extends OBPQueryParam
case class OBPAccountId(value: String) extends OBPQueryParam
case class OBPUrl(value: String) extends OBPQueryParam
case class OBPAppName(value: String) extends OBPQueryParam
case class OBPExcludeAppNames(values: List[String]) extends OBPQueryParam
case class OBPImplementedByPartialFunction(value: String) extends OBPQueryParam
case class OBPImplementedInVersion(value: String) extends OBPQueryParam
case class OBPVerb(value: String) extends OBPQueryParam
case class OBPAnon(value: Boolean) extends OBPQueryParam
case class OBPCorrelationId(value: String) extends OBPQueryParam
case class OBPDuration(value: Long) extends OBPQueryParam
case class OBPExcludeUrlPatterns(values: List[String]) extends OBPQueryParam
case class OBPExcludeImplementedByPartialFunctions(value: List[String]) extends OBPQueryParam
case class OBPFunctionName(value: String) extends OBPQueryParam
case class OBPConnectorName(value: String) extends OBPQueryParam
case class OBPEmpty() extends OBPQueryParam

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.api.v1_2
import java.util.Date
@ -45,9 +40,10 @@ case class HostedBy(
email : String,
phone : String
)
case class ErrorMessage(
error : String
)
case class ErrorMessage(code: Int,
message: String
)
case class SuccessMessage(
success : String
)

View File

@ -1859,7 +1859,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts" :: other_account_id :: "metadata" :: "corporate_location" :: Nil JsonPost json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
@ -1903,7 +1903,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts":: other_account_id :: "metadata" :: "corporate_location" :: Nil JsonPut json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
@ -1945,7 +1945,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts":: other_account_id :: "metadata" :: "corporate_location" :: Nil JsonDelete _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
@ -1989,7 +1989,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts" :: other_account_id :: "metadata" :: "physical_location" :: Nil JsonPost json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
@ -2034,7 +2034,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts":: other_account_id :: "metadata" :: "physical_location" :: Nil JsonPut json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
@ -2077,7 +2077,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "other_accounts":: other_account_id :: "metadata" :: "physical_location" :: Nil JsonDelete _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
account <- BankAccount(bankId, accountId) ?~! BankAccountNotFound
view <- Views.views.vend.view(viewId, BankIdAccountId(account.bankId, account.accountId))
otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, cc.user)
@ -2265,7 +2265,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "narrative" :: Nil JsonPost json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
narrativeJson <- tryo{json.extract[TransactionNarrativeJSON]} ?~ {InvalidJsonFormat}
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
addNarrative <- Box(metadata.addOwnerComment) ?~ { s"$NoViewPermission can_add_owner_comment. Current ViewId($viewId)" }
@ -2302,7 +2302,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "narrative" :: Nil JsonPut json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
narrativeJson <- tryo{json.extract[TransactionNarrativeJSON]} ?~ {InvalidJsonFormat}
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
addNarrative <- Box(metadata.addOwnerComment) ?~ { s"$NoViewPermission can_add_owner_comment. Current ViewId($viewId)" }
@ -2412,7 +2412,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "comments" :: Nil JsonPost json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
commentJson <- tryo{json.extract[PostTransactionCommentJSON]} ?~ {InvalidJsonFormat}
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
addCommentFunc <- Box(metadata.addComment) ?~ { s"$NoViewPermission can_add_comment. Current ViewId($viewId)" }
@ -2524,7 +2524,7 @@ trait APIMethods121 {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
tagJson <- tryo{json.extract[PostTransactionTagJSON]} ?~ { s"$InvalidJsonFormat Check your Post Json Body." }
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
addTagFunc <- Box(metadata.addTag) ?~ { s"$NoViewPermission can_add_tag. Current ViewId($viewId)" }
@ -2635,7 +2635,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "images" :: Nil JsonPost json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
imageJson <- tryo{json.extract[PostTransactionImageJSON]} ?~! InvalidJsonFormat
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, Full(u), Some(cc))
addImageFunc <- Box(metadata.addImage) ?~ { s"$NoViewPermission can_add_image. Current ViewId($viewId)" }
@ -2750,7 +2750,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "where" :: Nil JsonPost json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
view <- Views.views.vend.view(viewId, BankIdAccountId(bankId, accountId))
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, cc.user, Some(cc))
addWhereTag <- Box(metadata.addWhereTag) ?~ { s"$NoViewPermission can_add_where_tag. Current ViewId($viewId)" }
@ -2794,7 +2794,7 @@ trait APIMethods121 {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "where" :: Nil JsonPut json -> _ => {
cc =>
for {
u <- cc.user
u <- cc.user ?~ UserNotLoggedIn
view <- Views.views.vend.view(viewId, BankIdAccountId(bankId, accountId))
metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, cc.user, Some(cc))
addWhereTag <- Box(metadata.addWhereTag) ?~ { s"$NoViewPermission can_add_where_tag. Current ViewId($viewId)" }

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.api.v1_2_1
import java.util.Date
@ -51,8 +46,8 @@ case class HostedBy(
)
case class RateLimiting(enabled: Boolean, technology: String, service_available: Boolean, is_active: Boolean)
case class ErrorMessage(
error : String
case class ErrorMessage(code: Int,
message : String
)
case class SuccessMessage(
success : String

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,17 +16,12 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.v1_2_1

View File

@ -335,23 +335,25 @@ object JSONFactory1_4_0 {
// Used to describe the OBP API calls for documentation and API discovery purposes
case class ResourceDocJson(operation_id: String,
implemented_by: ImplementedByJson,
request_verb: String,
request_url: String,
summary: String,
description: String,
example_request_body: scala.Product,
success_response_body: scala.Product,
error_response_bodies: List[String],
is_core: Boolean,
is_psd2: Boolean,
is_obwg: Boolean,
tags: List[String],
typed_request_body: JValue, //JSON Schema --> https://spacetelescope.github.io/understanding-json-schema/index.html
typed_success_response_body: JValue, //JSON Schema --> https://spacetelescope.github.io/understanding-json-schema/index.html
roles: Option[List[ApiRole]] = None,
is_featured: Boolean,
special_instructions: String)
implemented_by: ImplementedByJson,
request_verb: String,
request_url: String,
summary: String,
description: String,
example_request_body: scala.Product,
success_response_body: scala.Product,
error_response_bodies: List[String],
is_core: Boolean,
is_psd2: Boolean,
is_obwg: Boolean,
tags: List[String],
typed_request_body: JValue, //JSON Schema --> https://spacetelescope.github.io/understanding-json-schema/index.html
typed_success_response_body: JValue, //JSON Schema --> https://spacetelescope.github.io/understanding-json-schema/index.html
roles: Option[List[ApiRole]] = None,
is_featured: Boolean,
special_instructions: String,
specified_url: String // Derived value. The Url when called under a certain version.
)
@ -367,7 +369,7 @@ object JSONFactory1_4_0 {
// We return html rather than markdown to the consumer so they don't have to bother with these questions.
// Set the timeout: https://github.com/sirthias/pegdown#parsing-timeouts
val PegDownProcessorTimeout: Long = 1000*20
val PegDownProcessorTimeout: Long = 1000*20
val pegDownProcessor : PegDownProcessor = new PegDownProcessor(PegDownProcessorTimeout)
ResourceDocJson(
@ -389,7 +391,8 @@ object JSONFactory1_4_0 {
typed_success_response_body = createTypedBody(rd.successResponseBody),
roles = rd.roles,
is_featured = rd.isFeatured,
special_instructions = pegDownProcessor.markdownToHtml(rd.specialInstructions.getOrElse("").stripMargin)
special_instructions = pegDownProcessor.markdownToHtml(rd.specialInstructions.getOrElse("").stripMargin),
specified_url = rd.specifiedUrl.getOrElse("")
)
}
@ -433,10 +436,10 @@ object JSONFactory1_4_0 {
//String --> Some field calleds `date`, we will treat the filed as a `date` object.
//String --> But for some are not, eg: `date_of_birth` and `future_date` in V300Custom
case i: String if(key.contains("date")&& value.toString.length != "20181230".length) => "\"" + key + """": {"type": "string","format": "date-time"}"""
case Some(i: String) if(key.contains("date")&& value.toString.length != "20181230".length) => "\"" + key + """": {"type": "string","format": "date-time"}"""
case List(i: String, _*) if(key.contains("date")&& value.toString.length != "20181230".length) => "\"" + key + """": {"type": "array","items": {"type": "string","format": "date-time"}}"""
case Some(List(i: String, _*)) if(key.contains("date")&& value.toString.length != "20181230".length) => "\"" + key + """": {"type": "array","items": {"type": "string","format": "date-time"}}"""
case i: String if(key.contains("date")&& i.length != "20181230".length) => "\"" + key + """": {"type": "string","format": "date-time"}"""
case Some(i: String) if(key.contains("date")&& i.length != "20181230".length) => "\"" + key + """": {"type": "string","format": "date-time"}"""
case List(i: String, _*) if(key.contains("date")&& i.length != "20181230".length) => "\"" + key + """": {"type": "array","items": {"type": "string","format": "date-time"}}"""
case Some(List(i: String, _*)) if(key.contains("date")&& i.length != "20181230".length) => "\"" + key + """": {"type": "array","items": {"type": "string","format": "date-time"}}"""
//String-->
case i: String => "\"" + key + """": {"type":"string"}"""

View File

@ -8,7 +8,8 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
import code.api.util.APIUtil._
import code.api.util.ApiTag._
import code.api.util.ErrorMessages.UserNotLoggedIn
import code.api.util.{APIUtil, ApiRole, ApiVersion, ErrorMessages}
import code.api.util.NewStyle.HttpCode
import code.api.util._
import code.api.v1_2_1.OBPAPI1_2_1._
import code.api.v1_2_1.{AmountOfMoneyJsonV121 => AmountOfMoneyJSON121, JSONFactory => JSONFactory121}
import code.api.v1_4_0.JSONFactory1_4_0
@ -265,7 +266,7 @@ trait APIMethods200 {
basicAccountsJSON,
List(BankNotFound, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagAccount, apiTagPrivateData, apiTagPublicData)
List(apiTagAccount, apiTagPrivateData, apiTagPublicData, apiTagNewStyle)
)
//TODO, double check with `lazy val privateAccountsAtOneBank`, they are the same accounts, only different json body.
@ -274,12 +275,12 @@ trait APIMethods200 {
case "banks" :: BankId(bankId) :: "accounts" :: Nil JsonGet req => {
cc =>
for{
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
(bank, callContext) <- Bank(bankId, Some(cc)) ?~! BankNotFound
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
} yield {
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
val availablePrivateAccounts = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
successJsonResponse(privateBankAccountBasicListToJson(availablePrivateAccounts, privateViewsUserCanAccessAtOneBank))
(privateBankAccountBasicListToJson(availablePrivateAccounts, privateViewsUserCanAccessAtOneBank), HttpCode.`200`(callContext))
}
}
}
@ -309,7 +310,7 @@ trait APIMethods200 {
coreAccountsJSON,
List(UserNotLoggedIn, UnknownError),
Catalogs(Core, PSD2, OBWG),
List(apiTagAccount, apiTagPrivateData))
List(apiTagAccount, apiTagPrivateData, apiTagNewStyle))
apiRelations += ApiRelation(corePrivateAccountsAtOneBank, createAccount, "new")
apiRelations += ApiRelation(corePrivateAccountsAtOneBank, corePrivateAccountsAtOneBank, "self")
@ -322,38 +323,40 @@ trait APIMethods200 {
case "my" :: "banks" :: BankId(bankId) :: "accounts" :: Nil JsonGet req => {
cc =>
for {
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
(bank, callContext) <- Bank(bankId, Some(cc))
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
} yield {
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
val privateAaccountsForOneBank = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
val result = corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
(result, HttpCode.`200`(callContext))
}
}
// Also we support accounts/private to maintain compatibility with 1.4.0
case "my" :: "banks" :: BankId(bankId) :: "accounts" :: "private" :: Nil JsonGet req => {
cc =>
for {
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
(bank, callContext) <- Bank(bankId, Some(cc))
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
} yield {
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
val privateAaccountsForOneBank = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
val result = corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
(result, HttpCode.`200`(callContext))
}
}
// Supports idea of default bank
case "bank" :: "accounts" :: Nil JsonGet req => {
cc =>
for {
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
(bank, callContext) <- Bank(BankId(defaultBankId), Some(cc)) ?~! ErrorMessages.DefaultBankIdNotSet
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
(bank, callContext) <- NewStyle.function.getBank(BankId(defaultBankId), callContext)
} yield {
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
val privateAaccountsForOneBank = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
val result = corePrivateAccountsAtOneBankResult(CallerContext(corePrivateAccountsAtOneBank), codeContext, u, privateAaccountsForOneBank, privateViewsUserCanAccessAtOneBank)
(result, HttpCode.`200`(callContext))
}
}
@ -381,7 +384,7 @@ trait APIMethods200 {
basicAccountsJSON,
List(UserNotLoggedIn, BankNotFound, UnknownError),
Catalogs(Core, PSD2, OBWG),
List(apiTagAccount)
List(apiTagAccount, apiTagNewStyle)
)
lazy val privateAccountsAtOneBank : OBPEndpoint = {
@ -389,12 +392,12 @@ trait APIMethods200 {
case "banks" :: BankId(bankId) :: "accounts" :: "private" :: Nil JsonGet req => {
cc =>
for {
u <- cc.user ?~! ErrorMessages.UserNotLoggedIn
(bank, callContext) <- Bank(bankId, Some(cc)) ?~! BankNotFound
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
} yield {
val privateViewsUserCanAccessAtOneBank = Views.views.vend.privateViewsUserCanAccess(u).filter(_.bankId == bankId)
val availablePrivateAccounts = bank.privateAccounts(privateViewsUserCanAccessAtOneBank)
successJsonResponse(privateBankAccountsListToJson(availablePrivateAccounts, privateViewsUserCanAccessAtOneBank))
(privateBankAccountsListToJson(availablePrivateAccounts, privateViewsUserCanAccessAtOneBank), HttpCode.`200`(callContext))
}
}
}
@ -420,18 +423,18 @@ trait APIMethods200 {
basicAccountsJSON,
List(UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagAccountPublic, apiTagAccount, apiTagPublicData))
List(apiTagAccountPublic, apiTagAccount, apiTagPublicData, apiTagNewStyle))
lazy val publicAccountsAtOneBank : OBPEndpoint = {
//get public accounts for a single bank
case "banks" :: BankId(bankId) :: "accounts" :: "public" :: Nil JsonGet req => {
cc =>
for {
(bank, callContext) <- Bank(bankId, Some(cc))
publicViewsForBank <- Full(Views.views.vend.publicViewsForBank(bank.bankId))
(bank, callContext) <- NewStyle.function.getBank(bankId, Some(cc))
} yield {
val publicViewsForBank = Views.views.vend.publicViewsForBank(bank.bankId)
val publicAccountsJson = publicBankAccountBasicListToJson(bank.publicAccounts(publicViewsForBank), publicViewsForBank)
successJsonResponse(publicAccountsJson)
(publicAccountsJson, HttpCode.`200`(callContext))
}
}
}
@ -942,7 +945,7 @@ trait APIMethods200 {
|More details about the data moderation by the view [here](#1_2_1-getViewsForBankAccount).
|
|PSD2 Context: PSD2 requires customers to have access to their account information via third party applications.
|This call provides balance and other account information via delegated authenticaiton using OAuth.
|This call provides balance and other account information via delegated authentication using OAuth.
|
|${authenticationRequiredMessage(true)} if the 'is_public' field in view (VIEW_ID) is not set to `true`.
|
@ -1171,7 +1174,7 @@ trait APIMethods200 {
)
lazy val getTransactionTypes : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "transaction-types" :: Nil JsonGet _ => {
case "banks" :: BankId(bankId) :: "" :: Nil JsonGet _ => {
cc => {
for {
// Get Transaction Types from the active provider

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.api.v2_0_0
import java.util.Date

View File

@ -1,34 +1,29 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2018, TESOBE Ltd
**
*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 Ltd
*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
*Stefan Bethge : stefan AT tesobe DOT com
*Everett Sochowski : everett AT tesobe DOT com
*Ayoub Benali: ayoub AT tesobe DOT com
*
*/
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd.
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 Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.v2_0_0
import code.api.OBPRestHelper

View File

@ -1016,7 +1016,7 @@ trait APIMethods210 {
)
} yield {
val cardJson = JSONFactory1_3_0.createPhysicalCardJSON(card, u)
successJsonResponse(Extraction.decompose(cardJson))
successJsonResponse(Extraction.decompose(cardJson), 201)
}
}
}
@ -1520,7 +1520,7 @@ trait APIMethods210 {
success <- Connector.connector.vend.createOrUpdateBranch(branch)
} yield {
val json = JSONFactory1_4_0.createBranchJson(success)
createdJsonResponse(Extraction.decompose(json))
createdJsonResponse(Extraction.decompose(json),201)
}
}
}
@ -1560,7 +1560,7 @@ trait APIMethods210 {
success <- Connector.connector.vend.createOrUpdateBranch(branch)
} yield {
val json = JSONFactory1_4_0.createBranchJson(success)
createdJsonResponse(Extraction.decompose(json))
createdJsonResponse(Extraction.decompose(json), 201)
}
}
}

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.api.v2_1_0
import java.lang
@ -101,7 +96,8 @@ case class TransactionRequestBodyCounterpartyJSON(
to: CounterpartyIdJson,
value: AmountOfMoneyJsonV121,
description: String,
charge_policy: String
charge_policy: String,
future_date: Option[String] = None
) extends TransactionRequestCommonBodyJSON
// the data from endpoint, extract as valid JSON
@ -109,7 +105,8 @@ case class TransactionRequestBodySEPAJSON(
value: AmountOfMoneyJsonV121,
to: IbanJson,
description: String,
charge_policy: String
charge_policy: String,
future_date: Option[String] = None
) extends TransactionRequestCommonBodyJSON
// Note: FreeForm is not used yet, the format maybe changed latter. the data from endpoint, extract as valid JSON

View File

@ -1,33 +1,28 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2018, TESOBE Ltd
**
*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 Ltd
*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
*Stefan Bethge : stefan AT tesobe DOT com
*Everett Sochowski : everett AT tesobe DOT com
*Ayoub Benali: ayoub AT tesobe DOT com
*
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd.
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 Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.v2_1_0

View File

@ -247,6 +247,9 @@ trait APIMethods220 {
cc =>
for {
(_, callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
_ <- Helper.booleanToFuture(failMsg = ConsumerHasMissingRoles + CanReadFx) {
checkScope(bankId.value, getConsumerPrimaryKey(callContext), ApiRole.canReadFx)
}
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
_ <- NewStyle.function.tryons(failMsg = InvalidISOCurrencyCode,400, callContext) {
assert(isValidCurrencyISOCode(fromCurrencyCode))
@ -374,6 +377,8 @@ trait APIMethods220 {
"Get Message Docs",
"""These message docs provide example messages sent by OBP to the (Kafka) message queue for processing by the Core Banking / Payment system Adapter - together with an example expected response and possible error codes.
| Integrators can use these messages to build Adapters that provide core banking services to OBP.
|
| Note: API Explorer provides a Message Docs page where these messages are displayed.
|
| `CONNECTOR`:kafka_vMar2017 , kafka_vJune2017, kafka_vSept2018 ...
""".stripMargin,
@ -721,7 +726,7 @@ trait APIMethods220 {
InvalidAccountInitialBalance,
InitialBalanceMustBeZero,
InvalidAccountBalanceCurrency,
AccountIdAlreadyExsits,
AccountIdAlreadyExists,
UnknownError
),
Catalogs(notCore, notPSD2, notOBWG),
@ -751,7 +756,7 @@ trait APIMethods220 {
initialBalanceAsNumber <- tryo {BigDecimal(initialBalanceAsString)} ?~! InvalidAccountInitialBalance
_ <- booleanToBox(0 == initialBalanceAsNumber) ?~! InitialBalanceMustBeZero
currency <- tryo (jsonBody.balance.currency) ?~!ErrorMessages.InvalidAccountBalanceCurrency
_ <- booleanToBox(BankAccount(bankId, accountId).isEmpty, AccountIdAlreadyExsits)
_ <- booleanToBox(BankAccount(bankId, accountId).isEmpty, AccountIdAlreadyExists)
bankAccount <- Connector.connector.vend.createSandboxBankAccount(
bankId,
accountId,

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.api.v2_2_0
//import code.api.v1_2_1.JSONFactory

View File

@ -1,34 +1,4 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2018, TESOBE Ltd
**
*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 Ltd
*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
*Stefan Bethge : stefan AT tesobe DOT com
*Everett Sochowski : everett AT tesobe DOT com
*Ayoub Benali: ayoub AT tesobe DOT com
*
*/
package code.api.v2_2_0
import code.api.OBPRestHelper

View File

@ -29,14 +29,14 @@ import code.util.Helper
import code.util.Helper.booleanToBox
import code.views.Views
import com.github.dwickern.macros.NameOf.nameOf
import net.liftweb.common.{Box, Empty, Full}
import net.liftweb.common._
import net.liftweb.http.S
import net.liftweb.http.js.JE.JsRaw
import net.liftweb.http.rest.RestHelper
import net.liftweb.json.{Extraction, compactRender}
import net.liftweb.util.Helpers.tryo
import scala.collection.immutable.Nil
import scala.collection.immutable.{List, Nil}
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@ -296,7 +296,7 @@ trait APIMethods300 {
|More details about the data moderation by the view [here](#1_2_1-getViewsForBankAccount).
|
|PSD2 Context: PSD2 requires customers to have access to their account information via third party applications.
|This call provides balance and other account information via delegated authenticaiton using OAuth.
|This call provides balance and other account information via delegated authentication using OAuth.
|
|Authentication is required if the 'is_public' field in view (VIEW_ID) is not set to `true`.
|""".stripMargin,
@ -342,7 +342,7 @@ trait APIMethods300 {
|
|
|PSD2 Context: PSD2 requires customers to have access to their account information via third party applications.
|This call provides balance and other account information via delegated authenticaiton using OAuth.
|This call provides balance and other account information via delegated authentication using OAuth.
|
|${authenticationRequiredMessage(false)}
|
@ -660,21 +660,20 @@ trait APIMethods300 {
lazy val getTransactionsForBankAccount: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: Nil JsonGet req => {
cc =>
val res =
for {
(user, callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
(bankAccount, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext)
view <- NewStyle.function.view(viewId, BankIdAccountId(bankAccount.bankId, bankAccount.accountId), callContext)
} yield {
for {
//Note: error handling and messages for getTransactionParams are in the sub method
params <- createQueriesByHttpParams(callContext.get.requestHeaders)
(transactions, callContext) <- bankAccount.getModeratedTransactions(user, view, callContext, params: _*)
} yield {
(createTransactionsJson(transactions), HttpCode.`200`(callContext))
}
for {
(user, callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
(bankAccount, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext)
view <- NewStyle.function.view(viewId, BankIdAccountId(bankAccount.bankId, bankAccount.accountId), callContext)
params <- createQueriesByHttpParamsFuture(callContext.get.requestHeaders)map {
unboxFullOrFail(_, callContext, InvalidFilterParameterFormat, 400)
}
res map { fullBoxOrException(_) } map { unboxFull(_) }
//Note: error handling and messages for getTransactionParams are in the sub method
(transactions, callContext) <- bankAccount.getModeratedTransactionsFuture(user, view, callContext, params: _*) map {
unboxFullOrFail(_, callContext, ConnectorEmptyResponse, 400)
}
} yield {
(createTransactionsJson(transactions), HttpCode.`200`(callContext))
}
}
}
@ -740,7 +739,7 @@ trait APIMethods300 {
} map { unboxFull(_) }
result: esw.APIResponse <- esw.searchProxyAsyncV300(u.userId, indexPart, bodyPart)
} yield {
(esw.parseResponse(result), HttpCode.`200`(callContext))
(esw.parseResponse(result), HttpCode.`201`(callContext))
}
}
}
@ -809,7 +808,7 @@ trait APIMethods300 {
} map { unboxFull(_) }
result <- esw.searchProxyStatsAsyncV300(u.userId, indexPart, bodyPart, field)
} yield {
(esw.parseResponse(result), HttpCode.`200`(callContext))
(esw.parseResponse(result), HttpCode.`201`(callContext))
}
}
}
@ -1011,7 +1010,7 @@ trait APIMethods300 {
success: Branches.BranchT <- Connector.connector.vend.createOrUpdateBranch(branch) ?~! {ErrorMessages.CountNotSaveOrUpdateResource + " Branch"}
} yield {
val json = JSONFactory300.createBranchJsonV300(success)
createdJsonResponse(Extraction.decompose(json))
createdJsonResponse(Extraction.decompose(json), 201)
}
}
}
@ -1066,7 +1065,7 @@ trait APIMethods300 {
success <- Connector.connector.vend.createOrUpdateAtm(atm)
} yield {
val json = JSONFactory300.createAtmJsonV300(success)
createdJsonResponse(Extraction.decompose(json))
createdJsonResponse(Extraction.decompose(json), 201)
}
}
}
@ -1189,6 +1188,8 @@ trait APIMethods300 {
val branchesWithLicense = for { branch <- list if branch.meta.license.name.size > 3 } yield branch
if (branchesWithLicense.size == 0) fullBoxOrException(Empty ?~! branchesNotFoundLicense)
else Full(branchesWithLicense)
case Failure(msg, t, c) => Failure(msg, t, c)
case ParamFailure(x,y,z,q) => ParamFailure(x,y,z,q)
} map { unboxFull(_) } map {
branches =>
// Before we slice we need to sort in order to keep consistent results
@ -1310,6 +1311,8 @@ trait APIMethods300 {
val branchesWithLicense = for { branch <- list if branch.meta.license.name.size > 3 } yield branch
if (branchesWithLicense.size == 0) fullBoxOrException(Empty ?~! atmsNotFoundLicense)
else Full(branchesWithLicense)
case Failure(msg, t, c) => Failure(msg, t, c)
case ParamFailure(x,y,z,q) => ParamFailure(x,y,z,q)
} map { unboxFull(_) } map {
branch =>
// Before we slice we need to sort in order to keep consistent results
@ -1612,7 +1615,7 @@ trait APIMethods300 {
nameOf(addEntitlementRequest),
"POST",
"/entitlement-requests",
"Add Entitlement Request for a Logged User.",
"Add Entitlement Request for current User.",
s"""Create Entitlement Request.
|
|Any logged in User can use this endpoint to request an Entitlement
@ -1668,7 +1671,7 @@ trait APIMethods300 {
x => fullBoxOrException(x ~> APIFailureNewStyle(EntitlementRequestCannotBeAdded, 400, callContext.map(_.toLight)))
} map { unboxFull(_) }
} yield {
(JSONFactory300.createEntitlementRequestJSON(addedEntitlementRequest), HttpCode.`200`(callContext))
(JSONFactory300.createEntitlementRequestJSON(addedEntitlementRequest), HttpCode.`201`(callContext))
}
}
}
@ -2056,7 +2059,7 @@ trait APIMethods300 {
UnknownError
),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagRole, apiTagEntitlement, apiTagUser, apiTagNewStyle),
List(apiTagScope, apiTagRole, apiTagNewStyle),
Some(List(canCreateScopeAtOneBank, canCreateScopeAtAnyBank)))
lazy val addScope : OBPEndpoint = {
@ -2105,7 +2108,7 @@ trait APIMethods300 {
addedEntitlement <- Future {Scope.scope.vend.addScope(postedData.bank_id, consumerId, postedData.role_name)} map { unboxFull(_) }
} yield {
(JSONFactory300.createScopeJson(addedEntitlement), HttpCode.`200`(callContext))
(JSONFactory300.createScopeJson(addedEntitlement), HttpCode.`201`(callContext))
}
}
}
@ -2128,7 +2131,7 @@ trait APIMethods300 {
emptyObjectJson,
List(UserNotLoggedIn, UserNotSuperAdmin, EntitlementNotFound, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagRole, apiTagUser, apiTagEntitlement, apiTagNewStyle))
List(apiTagScope, apiTagRole, apiTagEntitlement, apiTagNewStyle))
lazy val deleteScope: OBPEndpoint = {
case "consumers" :: consumerId :: "scope" :: scopeId :: Nil JsonDelete _ => {
@ -2170,7 +2173,7 @@ trait APIMethods300 {
scopeJsons,
List(UserNotLoggedIn, UserNotSuperAdmin, EntitlementNotFound, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagRole, apiTagUser, apiTagEntitlement, apiTagNewStyle))
List(apiTagScope, apiTagRole, apiTagEntitlement, apiTagNewStyle))
lazy val getScopes: OBPEndpoint = {
case "consumers" :: consumerId :: "scopes" :: Nil JsonGet _ => {

View File

@ -1,33 +1,28 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2018, TESOBE Ltd
**
*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 Ltd
*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
*Stefan Bethge : stefan AT tesobe DOT com
*Everett Sochowski : everett AT tesobe DOT com
*Ayoub Benali: ayoub AT tesobe DOT com
*
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd.
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 Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.v3_0_0

View File

@ -1,5 +1,7 @@
package code.api.v3_1_0
import java.util.UUID
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
import code.api.util.APIUtil._
import code.api.util.ApiRole._
@ -12,14 +14,14 @@ import code.api.v2_1_0.JSONFactory210
import code.api.v3_0_0.JSONFactory300
import code.api.v3_0_0.JSONFactory300.createAdapterInfoJson
import code.api.v3_1_0.JSONFactory310._
import code.bankconnectors.{Connector, OBPBankId}
import code.bankconnectors.Connector
import code.consumer.Consumers
import code.customer.{CreditLimit, CreditRating, CustomerFaceImage}
import code.entitlement.Entitlement
import code.loginattempts.LoginAttempt
import code.metrics.APIMetrics
import code.model._
import code.model.dataAccess.AuthUser
import code.model.dataAccess.{AuthUser, BankAccountCreation}
import code.productattribute.ProductAttribute.ProductAttributeType
import code.products.Products.ProductCode
import code.users.Users
@ -32,6 +34,7 @@ import net.liftweb.http.provider.HTTPParam
import net.liftweb.http.rest.RestHelper
import net.liftweb.util.Helpers
import net.liftweb.util.Helpers.tryo
import org.apache.commons.lang3.Validate
import scala.collection.immutable.{List, Nil}
import scala.collection.mutable.ArrayBuffer
@ -41,7 +44,9 @@ import scala.concurrent.Future
trait APIMethods310 {
self: RestHelper =>
val Implementations3_1_0 = new Object() {
val Implementations3_1_0 = new Implementations310()
class Implementations310 {
val implementedInApiVersion: ApiVersion = ApiVersion.v3_1_0
@ -155,7 +160,7 @@ trait APIMethods310 {
// banksBox <- Connector.connector.vend.getBanksFuture()
// banks <- unboxFullAndWrapIntoFuture{ banksBox }
// } yield
Future{ (JSONFactory310.createCreditLimitOrderResponseJson(), HttpCode.`200`(cc))}
Future{ (JSONFactory310.createCreditLimitOrderResponseJson(), HttpCode.`201`(Some(cc)))}
}
}
@ -494,7 +499,10 @@ trait APIMethods310 {
"/users/USERNAME/lock-status",
"Unlock the user",
s"""
|Get Customers that has a firehose View.
|Unlock a User.
|
|(Perhaps the user was locked due to multiple failed login attempts)
|
|${authenticationRequiredMessage(true)}
|
|""".stripMargin,
@ -821,13 +829,13 @@ trait APIMethods310 {
}
val accountWebHookInfo = """Webhooks are used to call external URLs when certain events happen.
val accountWebHookInfo = s"""Webhooks are used to call external URLs when certain events happen.
|
|Account Webhooks focus on events around accounts.
|
|For instance, a webhook could be used to notify an external serivce if a balance changes on an account.
|For instance, a webhook could be used to notify an external service if a balance changes on an account.
|
|This functionality is work in progress! Although you can create and modify Webhooks, they do not yet fire on triggers."""
|This functionality is work in progress! Please note that only implemented trigger is: ${ApiTrigger.onBalanceChange}"""
resourceDocs += ResourceDoc(
@ -875,12 +883,13 @@ trait APIMethods310 {
triggerName = postJson.trigger_name,
url = postJson.url,
httpMethod = postJson.http_method,
httpProtocol = postJson.http_protocol,
isActive = isActive
) map {
unboxFullOrFail(_, callContext, CreateWebhookError, 400)
}
} yield {
(createAccountWebhookJson(wh), HttpCode.`200`(callContext))
(createAccountWebhookJson(wh), HttpCode.`201`(callContext))
}
}
}
@ -892,8 +901,8 @@ trait APIMethods310 {
nameOf(enableDisableAccountWebhook),
"PUT",
"/banks/BANK_ID/account-web-hooks",
"Update an Account Webhook",
s"""Update an Account Webhook
"Enable/Disable an Account Webhook",
s"""Enable/Disable an Account Webhook
|
|
|$accountWebHookInfo
@ -903,7 +912,7 @@ trait APIMethods310 {
List(UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
apiTagWebhook :: apiTagBank :: apiTagNewStyle :: Nil,
Some(List(canCreateWebhook))
Some(List(canUpdateWebhook))
)
lazy val enableDisableAccountWebhook : OBPEndpoint = {
@ -1080,7 +1089,7 @@ trait APIMethods310 {
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
(account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext)
view <- NewStyle.function.view(viewId, BankIdAccountId(account.bankId, account.accountId), callContext)
(moderatedTransaction, callContext) <- Future(account.moderatedTransaction(transactionId, view, user, callContext)) map {
(moderatedTransaction, callContext) <- account.moderatedTransactionFuture(bankId, accountId, transactionId, view, user, callContext) map {
unboxFullOrFail(_, callContext, GetTransactionsException)
}
} yield {
@ -1226,7 +1235,7 @@ trait APIMethods310 {
unboxFullOrFail(_, callContext, CreateCustomerError, 400)
}
} yield {
(JSONFactory310.createCustomerJson(customer), HttpCode.`200`(callContext))
(JSONFactory310.createCustomerJson(customer), HttpCode.`201`(callContext))
}
}
}
@ -1347,7 +1356,7 @@ trait APIMethods310 {
}
(customer, callContext) <- NewStyle.function.getCustomerByCustomerNumber(postedData.customer_number, bank.bankId, callContext)
} yield {
(JSONFactory310.createCustomerJson(customer), HttpCode.`200`(callContext))
(JSONFactory310.createCustomerJson(customer), HttpCode.`201`(callContext))
}
}
}
@ -1386,7 +1395,7 @@ trait APIMethods310 {
(_, callContext) <- NewStyle.function.findByUserId(userId, callContext)
(userAuthContext, callContext) <- NewStyle.function.createUserAuthContext(userId, postedData.key, postedData.value, callContext)
} yield {
(JSONFactory310.createUserAuthContextJson(userAuthContext), HttpCode.`200`(callContext))
(JSONFactory310.createUserAuthContextJson(userAuthContext), HttpCode.`201`(callContext))
}
}
}
@ -1543,7 +1552,7 @@ trait APIMethods310 {
(_, callContext) <- NewStyle.function.getCustomerByCustomerId(customerId, callContext)
(taxResidence, callContext) <- NewStyle.function.createTaxResidence(customerId, postedData.domain, postedData.tax_number, callContext)
} yield {
(JSONFactory310.createTaxResidence(List(taxResidence)), HttpCode.`200`(callContext))
(JSONFactory310.createTaxResidence(List(taxResidence)), HttpCode.`201`(callContext))
}
}
}
@ -1719,7 +1728,7 @@ trait APIMethods310 {
postedData.state,
callContext)
} yield {
(JSONFactory310.createAddress(address), HttpCode.`200`(callContext))
(JSONFactory310.createAddress(address), HttpCode.`201`(callContext))
}
}
}
@ -1868,7 +1877,7 @@ trait APIMethods310 {
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
_ <- NewStyle.function.hasEntitlement(failMsg = UserHasMissingRoles + CanRefreshUser)("", userId, canRefreshUser)
startTime <- Future{Helpers.now}
_ <- NewStyle.function.findByUserId(userId, Some(cc))
_ <- NewStyle.function.findByUserId(userId, callContext)
_ <- if (APIUtil.isSandboxMode) Future{} else Future{ tryo {AuthUser.updateUserAccountViews(u, callContext)}} map {
unboxFullOrFail(_, callContext, RefreshUserError, 400)
}
@ -2062,6 +2071,238 @@ trait APIMethods310 {
}
}
resourceDocs += ResourceDoc(
createAccountApplication,
implementedInApiVersion,
nameOf(createAccountApplication),
"POST",
"/banks/BANK_ID/account-applications",
"Create Account Application",
s""" Create Account Application
|
|${authenticationRequiredMessage(true)}
|
|""",
accountApplicationJson,
accountApplicationResponseJson,
List(
InvalidJsonFormat,
UnknownError
),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagAccountApplication, apiTagAccount, apiTagNewStyle))
lazy val createAccountApplication : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "account-applications" :: Nil JsonPost json -> _=> {
cc =>
for {
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
failMsg = s"$InvalidJsonFormat The Json body should be the $AccountApplicationJson "
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[AccountApplicationJson]
}
illegalProductCodeMsg = s"$InvalidJsonFormat product_code should not be empty."
_ <- NewStyle.function.tryons(illegalProductCodeMsg, 400, callContext) {
Validate.notBlank(postedData.product_code)
}
illegalUserIdOrCustomerIdMsg = s"$InvalidJsonFormat User_id and customer_id should not both are empty."
_ <- NewStyle.function.tryons(illegalUserIdOrCustomerIdMsg, 400, callContext) {
Validate.isTrue(postedData.user_id.isDefined || postedData.customer_id.isDefined)
}
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
user <- unboxOptionOBPReturnType(postedData.user_id.map(NewStyle.function.findByUserId(_, callContext)))
customer <- unboxOptionOBPReturnType(postedData.customer_id.map(NewStyle.function.getCustomerByCustomerId(_, callContext)))
(productAttribute, callContext) <- NewStyle.function.createAccountApplication(
productCode = ProductCode(postedData.product_code),
userId = postedData.user_id,
customerId = postedData.customer_id,
callContext = callContext
)
} yield {
(createAccountApplicationJson(productAttribute, user, customer), HttpCode.`201`(callContext))
}
}
}
resourceDocs += ResourceDoc(
getAccountApplications,
implementedInApiVersion,
nameOf(getAccountApplications),
"GET",
"/banks/BANK_ID/account-applications",
"Get Account Applications",
s"""Get the Account Applications.
|
|
|${authenticationRequiredMessage(true)}
|
|""",
emptyObjectJson,
accountApplicationsJsonV310,
List(
UserNotLoggedIn,
UserHasMissingRoles,
UnknownError
),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagAccountApplication, apiTagAccount, apiTagNewStyle))
lazy val getAccountApplications : OBPEndpoint = {
case "banks" :: BankId(bankId) ::"account-applications" :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
_ <- NewStyle.function.hasEntitlement(failMsg = UserHasMissingRoles + CanGetAccountApplications)("", u.userId, canGetAccountApplications)
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
(accountApplications, _) <- NewStyle.function.getAllAccountApplication(callContext)
(users, _) <- NewStyle.function.findUsers(accountApplications.map(_.userId), callContext)
(customers, _) <- NewStyle.function.findCustomers(accountApplications.map(_.customerId), callContext)
} yield {
(JSONFactory310.createAccountApplications(accountApplications, users, customers), HttpCode.`200`(callContext))
}
}
}
resourceDocs += ResourceDoc(
getAccountApplication,
implementedInApiVersion,
nameOf(getAccountApplication),
"GET",
"/banks/BANK_ID/account-applications/ACCOUNT_APPLICATION_ID",
"Get Account Application by Id",
s"""Get the Account Application.
|
|
|${authenticationRequiredMessage(true)}
|
|""",
emptyObjectJson,
accountApplicationResponseJson,
List(
UserNotLoggedIn,
UserHasMissingRoles,
UnknownError
),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagAccountApplication, apiTagAccount, apiTagNewStyle))
lazy val getAccountApplication : OBPEndpoint = {
case "banks" :: BankId(bankId) ::"account-applications":: accountApplicationId :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
(accountApplication, _) <- NewStyle.function.getAccountApplicationById(accountApplicationId, callContext)
userId = Option(accountApplication.userId)
customerId = Option(accountApplication.customerId)
user <- unboxOptionOBPReturnType(userId.map(NewStyle.function.findByUserId(_, callContext)))
customer <- unboxOptionOBPReturnType(customerId.map(NewStyle.function.getCustomerByCustomerId(_, callContext)))
} yield {
(createAccountApplicationJson(accountApplication, user, customer), HttpCode.`200`(callContext))
}
}
}
resourceDocs += ResourceDoc(
updateAccountApplicationStatus,
implementedInApiVersion,
nameOf(updateAccountApplicationStatus),
"PUT",
"/banks/BANK_ID/account-applications/ACCOUNT_APPLICATION_ID",
"Update Account Application Status",
s"""Update an Account Application status
|
|
|${authenticationRequiredMessage(true)}
|
|""",
accountApplicationUpdateStatusJson,
accountApplicationResponseJson,
List(
UserNotLoggedIn,
UserHasMissingRoles,
UnknownError
),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagAccountApplication, apiTagAccount, apiTagNewStyle)
)
lazy val updateAccountApplicationStatus : OBPEndpoint = {
case "banks" :: BankId(bankId) ::"account-applications" :: accountApplicationId :: Nil JsonPut json -> _ => {
cc =>
for {
(Full(u), callContext) <- authorizeEndpoint(UserNotLoggedIn, cc)
_ <- NewStyle.function.hasEntitlement(failMsg = UserHasMissingRoles + CanUpdateAccountApplications)("", u.userId, ApiRole.canUpdateAccountApplications)
failMsg = s"$InvalidJsonFormat The Json body should be the $AccountApplicationUpdateStatusJson "
putJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[AccountApplicationUpdateStatusJson]
}
failMsg = s"$InvalidJsonFormat status should not be blank."
status <- NewStyle.function.tryons(failMsg, 400, callContext) {
Validate.notBlank(putJson.status)
}
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
(accountApplication, _) <- NewStyle.function.getAccountApplicationById(accountApplicationId, callContext)
(accountApplication, _) <- NewStyle.function.updateAccountApplicationStatus(accountApplicationId, status, callContext)
userId = Option(accountApplication.userId)
customerId = Option(accountApplication.customerId)
user <- unboxOptionOBPReturnType(userId.map(NewStyle.function.findByUserId(_, callContext)))
customer <- unboxOptionOBPReturnType(customerId.map(NewStyle.function.getCustomerByCustomerId(_, callContext)))
_ <- status match {
case "ACCEPTED" =>
for{
accountId <- Future{AccountId(UUID.randomUUID().toString)}
(_, callContext) <- NewStyle.function.createSandboxBankAccount(
bankId,
accountId,
accountApplication.productCode.value,
"",
"EUR",
BigDecimal("0"),
u.name,
"",
"",
"",
callContext)
}yield {
BankAccountCreation.setAsOwner(bankId, accountId, u)
}
case _ => Future{""}
}
} yield {
(createAccountApplicationJson(accountApplication, user, customer), HttpCode.`200`(callContext))
}
}
}
}
}

View File

@ -27,8 +27,9 @@ Berlin 13359, Germany
package code.api.v3_1_0
import java.lang
import java.util.Date
import java.util.{Date, Objects}
import code.accountapplication.AccountApplication
import code.customeraddress.CustomerAddress
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
import code.api.util.RateLimitPeriod.LimitCallPeriod
@ -49,6 +50,7 @@ import scala.collection.immutable.List
import code.customer.Customer
import code.context.UserAuthContext
import code.entitlement.Entitlement
import code.model.dataAccess.ResourceUser
import code.productattribute.ProductAttribute.ProductAttribute
import code.taxresidence.TaxResidence
@ -181,6 +183,7 @@ case class AccountWebhookJson(account_webhook_id: String,
trigger_name: String,
url: String,
http_method: String,
http_protocol: String,
created_by_user_id: String,
is_active: Boolean
)
@ -189,6 +192,7 @@ case class AccountWebhookPostJson(account_id: String,
trigger_name: String,
url: String,
http_method: String,
http_protocol: String,
is_active: String
)
case class AccountWebhookPutJson(account_webhook_id: String,
@ -326,7 +330,24 @@ case class ProductAttributeResponseJson(
value: String,
)
case class AccountApplicationJson(
product_code: String,
user_id: Option[String],
customer_id: Option[String]
)
case class AccountApplicationResponseJson(
account_application_id: String,
product_code: String,
user: ResourceUserJSON,
customer: CustomerJsonV310,
date_of_application: Date,
status: String
)
case class AccountApplicationUpdateStatusJson(status: String)
case class AccountApplicationsJsonV310(account_applications: List[AccountApplicationResponseJson])
case class RateLimitingInfoV310(enabled: Boolean, technology: String, service_available: Boolean, is_active: Boolean)
@ -442,6 +463,7 @@ object JSONFactory310{
trigger_name = wh.triggerName,
url = wh.url,
http_method = wh.httpMethod,
http_protocol = wh.httpProtocol,
created_by_user_id = wh.createdByUserId,
is_active = wh.isActive()
)
@ -570,5 +592,35 @@ object JSONFactory310{
`type` = productAttribute.attributeType.toString,
value = productAttribute.value,
)
def createAccountApplicationJson(accountApplication: AccountApplication, user: Box[User], customer: Box[Customer]): AccountApplicationResponseJson = {
val userJson = user.map(u => ResourceUserJSON(
user_id = u.userId,
email = u.emailAddress,
provider_id = u.idGivenByProvider,
provider = u.provider,
username = u.name
)).orNull
val customerJson = customer.map(createCustomerJson).orNull
AccountApplicationResponseJson(
account_application_id = accountApplication.accountApplicationId,
product_code = accountApplication.productCode.value,
user = userJson,
customer = customerJson,
date_of_application =accountApplication.dateOfApplication,
status = accountApplication.status
)
}
def createAccountApplications(accountApplications: List[AccountApplication], users: List[User], customers: List[Customer]): AccountApplicationsJsonV310 = {
val applicationList = accountApplications.map { x =>
val user = Box(users.find(it => it.userId == x.userId))
val customer = Box(customers.find(it => it.customerId == x.customerId))
createAccountApplicationJson(x, user, customer)
}
AccountApplicationsJsonV310(applicationList)
}
}

View File

@ -1,33 +1,28 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2018, TESOBE Ltd
**
*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 Ltd
*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
*Stefan Bethge : stefan AT tesobe DOT com
*Everett Sochowski : everett AT tesobe DOT com
*Ayoub Benali: ayoub AT tesobe DOT com
*
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd.
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 Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.v3_1_0
@ -324,6 +319,10 @@ object OBPAPI3_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
Implementations3_1_0.getProductAttribute ::
Implementations3_1_0.updateProductAttribute ::
Implementations3_1_0.deleteProductAttribute ::
Implementations3_1_0.createAccountApplication ::
Implementations3_1_0.getAccountApplications ::
Implementations3_1_0.getAccountApplication ::
Implementations3_1_0.updateAccountApplicationStatus ::
Nil
val allResourceDocs = Implementations3_1_0.resourceDocs ++

View File

@ -4,10 +4,10 @@ package code.atms
// Need to import these one by one because in same package!
import code.api.util.OBPQueryParam
import code.atms.Atms.{AtmId, AtmT}
import code.bankconnectors.OBPQueryParam
import code.model.BankId
import code.common._
import code.model.BankId
import net.liftweb.common.Logger
import net.liftweb.util.SimpleInjector

View File

@ -1,7 +1,7 @@
package code.atms
import code.api.util.{OBPLimit, OBPOffset, OBPQueryParam}
import code.atms.Atms._
import code.bankconnectors.{OBPLimit, OBPOffset, OBPQueryParam}
import code.common._
import code.model.BankId
import code.util.{TwentyFourHourClockString, UUIDString}

View File

@ -1,26 +1,27 @@
package code.bankconnectors
import java.util.{Date, UUID}
import java.util.Date
import code.accountapplication.AccountApplication
import code.accountholder.{AccountHolders, MapperAccountHolders}
import code.customeraddress.CustomerAddress
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.accountId
import code.api.util.APIUtil._
import code.api.util.ApiRole._
import code.api.util.ErrorMessages._
import code.api.util.{APIUtil, CallContext, ErrorMessages, NewStyle}
import code.api.util._
import code.api.v1_2_1.AmountOfMoneyJsonV121
import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140
import code.api.v2_1_0.{TransactionRequestCommonBodyJSON, _}
import code.api.v3_1_0._
import code.atms.Atms
import code.atms.Atms.{AtmId, AtmT}
import code.bankconnectors.akka.AkkaConnector_vDec2018
import code.bankconnectors.vJune2017.KafkaMappedConnector_vJune2017
import code.bankconnectors.vMar2017.{InboundAdapterInfoInternal, KafkaMappedConnector_vMar2017}
import code.bankconnectors.vSept2018.KafkaMappedConnector_vSept2018
import code.branches.Branches.{Branch, BranchId, BranchT}
import code.context.UserAuthContext
import code.customer._
import code.customeraddress.CustomerAddress
import code.fx.FXRate
import code.kafka.Topics.TopicTrait
import code.management.ImporterAPI.ImporterTransaction
@ -40,15 +41,14 @@ import code.views.Views
import net.liftweb.common.{Box, Empty, Failure, Full}
import net.liftweb.mapper.By
import net.liftweb.util.Helpers.tryo
import net.liftweb.util.{BCrypt, Helpers, Props, SimpleInjector}
import net.liftweb.util.{BCrypt, Helpers, SimpleInjector}
import scala.collection.immutable.List
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Await, Future}
import scala.concurrent.Future
import scala.math.BigInt
import scala.util.Random
import scala.concurrent.duration._
/*
So we can switch between different sources of resources e.g.
@ -65,11 +65,10 @@ Could consider a Map of ("resourceType" -> "provider") - this could tell us whic
*/
object Connector extends SimpleInjector {
import scala.reflect.runtime.universe._
def getConnectorInstance(connectorVersion: String):Connector = {
connectorVersion match {
case "mapped" => LocalMappedConnector
case "akka_vDec2018" => AkkaConnector_vDec2018
case "mongodb" => LocalRecordConnector
case "obpjvm" => ObpJvmMappedConnector
case "kafka" => KafkaMappedConnector
@ -91,41 +90,6 @@ object Connector extends SimpleInjector {
}
class OBPQueryParam
trait OBPOrder { def orderValue : Int }
object OBPOrder {
def apply(s: Option[String]): OBPOrder = s match {
case Some("asc") => OBPAscending
case Some("ASC")=> OBPAscending
case _ => OBPDescending
}
}
object OBPAscending extends OBPOrder { def orderValue = 1 }
object OBPDescending extends OBPOrder { def orderValue = -1}
case class OBPLimit(value: Int) extends OBPQueryParam
case class OBPOffset(value: Int) extends OBPQueryParam
case class OBPFromDate(value: Date) extends OBPQueryParam
case class OBPToDate(value: Date) extends OBPQueryParam
case class OBPOrdering(field: Option[String], order: OBPOrder) extends OBPQueryParam
case class OBPConsumerId(value: String) extends OBPQueryParam
case class OBPUserId(value: String) extends OBPQueryParam
case class OBPBankId(value: String) extends OBPQueryParam
case class OBPAccountId(value: String) extends OBPQueryParam
case class OBPUrl(value: String) extends OBPQueryParam
case class OBPAppName(value: String) extends OBPQueryParam
case class OBPExcludeAppNames(values: List[String]) extends OBPQueryParam
case class OBPImplementedByPartialFunction(value: String) extends OBPQueryParam
case class OBPImplementedInVersion(value: String) extends OBPQueryParam
case class OBPVerb(value: String) extends OBPQueryParam
case class OBPAnon(value: Boolean) extends OBPQueryParam
case class OBPCorrelationId(value: String) extends OBPQueryParam
case class OBPDuration(value: Long) extends OBPQueryParam
case class OBPExcludeUrlPatterns(values: List[String]) extends OBPQueryParam
case class OBPExcludeImplementedByPartialFunctions(value: List[String]) extends OBPQueryParam
case class OBPFunctionName(value: String) extends OBPQueryParam
case class OBPConnectorName(value: String) extends OBPQueryParam
case class OBPEmpty() extends OBPQueryParam
//Note: this is used for connector method: 'def getUser(name: String, password: String): Box[InboundUser]'
case class InboundUser(
email: String,
@ -217,6 +181,7 @@ trait Connector extends MdcLoggable{
}
def getAdapterInfo(callContext: Option[CallContext]) : Box[(InboundAdapterInfoInternal, Option[CallContext])] = Failure(NotImplemented + currentMethodName)
def getAdapterInfoFuture(callContext: Option[CallContext]) : Future[Box[(InboundAdapterInfoInternal, Option[CallContext])]] = Future(Failure(NotImplemented + "getAdapterInfoFuture"))
// Gets current challenge level for transaction request
// Transaction request challenge threshold. Level at which challenge is created and needs to be answered
@ -350,6 +315,7 @@ trait Connector extends MdcLoggable{
def getCoreBankAccountsHeldFuture(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Future[Box[List[AccountHeld]]]= Future {Failure(NotImplemented + currentMethodName)}
def checkBankAccountExists(bankId : BankId, accountId : AccountId, callContext: Option[CallContext] = None) : Box[(BankAccount, Option[CallContext])]= Failure(NotImplemented + currentMethodName)
def checkBankAccountExistsFuture(bankId : BankId, accountId : AccountId, callContext: Option[CallContext] = None) : Future[Box[(BankAccount, Option[CallContext])]] = Future {Failure(NotImplemented + currentMethodName)}
/**
* This method is just return an empty account to AccountType.
@ -400,6 +366,8 @@ trait Connector extends MdcLoggable{
def getCounterparties(thisBankId: BankId, thisAccountId: AccountId,viewId :ViewId, callContext: Option[CallContext] = None): Box[(List[CounterpartyTrait], Option[CallContext])]= Failure(NotImplemented + currentMethodName)
def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId,viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = Future {(Failure(NotImplemented + currentMethodName), callContext)}
def getTransactions(bankId: BankId, accountID: AccountId, queryParams: OBPQueryParam*): Box[List[Transaction]]= {
getTransactions(bankId, accountID, None, queryParams: _*).map(_._1)
}
@ -407,9 +375,17 @@ trait Connector extends MdcLoggable{
//TODO, here is a problem for return value `List[Transaction]`, this is a normal class, not a trait. It is a big class,
// it contains thisAccount(BankAccount object) and otherAccount(Counterparty object)
def getTransactions(bankId: BankId, accountID: AccountId, callContext: Option[CallContext], queryParams: OBPQueryParam*): Box[(List[Transaction], Option[CallContext])]= Failure(NotImplemented + currentMethodName)
def getTransactionsFuture(bankId: BankId, accountID: AccountId, callContext: Option[CallContext], queryParams: OBPQueryParam*): OBPReturnType[Box[List[Transaction]]] = {
val result: Box[(List[Transaction], Option[CallContext])] = getTransactions(bankId, accountID, callContext, queryParams: _*)
Future(result.map(_._1), result.map(_._2).getOrElse(callContext))
}
def getTransactionsCore(bankId: BankId, accountID: AccountId, callContext: Option[CallContext], queryParams: OBPQueryParam*): Box[(List[TransactionCore], Option[CallContext])]= Failure(NotImplemented + currentMethodName)
def getTransaction(bankId: BankId, accountID : AccountId, transactionId : TransactionId, callContext: Option[CallContext] = None): Box[(Transaction, Option[CallContext])] = Failure(NotImplemented + currentMethodName)
def getTransactionFuture(bankId: BankId, accountID : AccountId, transactionId : TransactionId, callContext: Option[CallContext] = None): OBPReturnType[Box[Transaction]] = {
val result: Box[(Transaction, Option[CallContext])] = getTransaction(bankId, accountID, transactionId, callContext)
Future(result.map(_._1), result.map(_._2).getOrElse(callContext))
}
def getPhysicalCards(user : User) : Box[List[PhysicalCard]] = Failure(NotImplemented + currentMethodName)
@ -1025,7 +1001,8 @@ trait Connector extends MdcLoggable{
to = CounterpartyIdJson(counterpartyId.value),
value = AmountOfMoneyJsonV121(body.value.currency, body.value.amount),
description = body.description,
charge_policy = transactionRequest.charge_policy)
charge_policy = transactionRequest.charge_policy,
future_date = transactionRequest.future_date)
(transactionId, callContext) <- NewStyle.function.makePaymentv210(
fromAccount,
@ -1052,7 +1029,8 @@ trait Connector extends MdcLoggable{
to = IbanJson(toCounterpartyIBan),
value = AmountOfMoneyJsonV121(body.value.currency, body.value.amount),
description = body.description,
charge_policy = transactionRequest.charge_policy
charge_policy = transactionRequest.charge_policy,
future_date = transactionRequest.future_date
)
(transactionId, callContext) <- NewStyle.function.makePaymentv210(
fromAccount,
@ -1545,4 +1523,20 @@ trait Connector extends MdcLoggable{
productAttributeId: String,
callContext: Option[CallContext]
): OBPReturnType[Box[Boolean]] = Future{(Failure(NotImplemented + currentMethodName), callContext)}
def createAccountApplication(
productCode: ProductCode,
userId: Option[String],
customerId: Option[String],
callContext: Option[CallContext]
): OBPReturnType[Box[AccountApplication]] = Future{(Failure(NotImplemented + currentMethodName), callContext)}
def getAllAccountApplication(callContext: Option[CallContext]): OBPReturnType[Box[List[AccountApplication]]] =
Future{(Failure(NotImplemented + currentMethodName), callContext)}
def getAccountApplicationById(accountApplicationId: String, callContext: Option[CallContext]): OBPReturnType[Box[AccountApplication]] =
Future{(Failure(NotImplemented + currentMethodName), callContext)}
def updateAccountApplicationStatus(accountApplicationId:String, status: String, callContext: Option[CallContext]): OBPReturnType[Box[AccountApplication]] =
Future{(Failure(NotImplemented + currentMethodName), callContext)}
}

View File

@ -28,7 +28,7 @@ import java.util.{Date, Locale, UUID}
import code.api.util.ErrorMessages._
import code.accountholder.AccountHolders
import code.api.util.{APIUtil, CallContext, ErrorMessages}
import code.api.util._
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
import code.bankconnectors.vMar2017.{InboundAdapterInfoInternal, KafkaMappedConnector_vMar2017}
import code.branches.Branches.{Branch, BranchT}

View File

@ -27,18 +27,20 @@ import java.text.SimpleDateFormat
import java.time.ZoneOffset.UTC
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.{Date, Locale, UUID}
import java.util.Date
import java.util.UUID.randomUUID
import code.accountholder.AccountHolders
import code.api.cache.Caching
import code.api.util.APIUtil.saveConnectorMetric
import code.api.util.ErrorMessages._
import code.api.util.{APIUtil, CallContext, ErrorMessages}
import code.api.util._
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
import code.atms.Atms.{AtmId, AtmT}
import code.atms.{Atms, MappedAtm}
import code.bankconnectors.vMar2017.{InboundAdapterInfoInternal, KafkaMappedConnector_vMar2017}
import code.branches.Branches.{Branch, BranchId, BranchT}
import code.fx.{FXRate, fx}
import code.bankconnectors.vMar2017.KafkaMappedConnector_vMar2017
import code.branches.Branches.{Branch, BranchT}
import code.fx.FXRate
import code.kafka.KafkaHelper
import code.management.ImporterAPI.ImporterTransaction
import code.metadata.comments.Comments
@ -57,7 +59,7 @@ import code.transactionrequests.{MappedTransactionRequestTypeCharge, Transaction
import code.util.Helper
import code.util.Helper.MdcLoggable
import code.views.Views
import com.google.common.cache.CacheBuilder
import com.tesobe.CacheKeyFromArguments
import com.tesobe.obp.transport.Pager
import com.tesobe.obp.transport.spi.{DefaultPager, DefaultSorter, TimestampFilter}
import net.liftweb.common._
@ -67,15 +69,9 @@ import net.liftweb.mapper._
import net.liftweb.util.Helpers._
import scala.collection.immutable.{List, Seq}
import scala.concurrent.TimeoutException
import scala.concurrent.duration._
import scala.concurrent.{Future, TimeoutException}
import scala.language.postfixOps
import scalacache.ScalaCache
import scalacache.guava.GuavaCache
import scalacache.memoization._
import java.util.UUID.randomUUID
import code.api.cache.Caching
import com.tesobe.CacheKeyFromArguments
object KafkaMappedConnector_JVMcompatible extends Connector with KafkaHelper with MdcLoggable {
@ -330,7 +326,7 @@ object KafkaMappedConnector_JVMcompatible extends Connector with KafkaHelper wit
} yield {
val res = tryExtract[List[KafkaInboundAccount]](process(req)) match {
case Full(a) => a
case Empty => List.empty
case _ => List.empty
}
logger.debug(s"JVMCompatible updateUserAccountViews got response ${res}")
res

View File

@ -3,12 +3,13 @@ package code.bankconnectors
import java.util.UUID.randomUUID
import java.util.{Date, UUID}
import code.accountapplication.AccountApplication
import code.customeraddress.{CustomerAddress, MappedCustomerAddress}
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
import code.api.cache.Caching
import code.api.util.APIUtil.{OBPReturnType, saveConnectorMetric, stringOrNull}
import code.api.util.ErrorMessages._
import code.api.util.{APIUtil, CallContext, ErrorMessages, NewStyle}
import code.api.util._
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
import code.api.v3_1_0.{CardObjectJson, CheckbookOrdersJson, PostCustomerJsonV310, TaxResidenceV310}
import code.atms.Atms.{AtmId, AtmT}
@ -371,15 +372,19 @@ object LocalMappedConnector extends Connector with MdcLoggable {
}
override def getBankAccount(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]): Box[(BankAccount, Option[CallContext])] = {
getBankAccountCommon(bankId, accountId, callContext)
}
def getBankAccountCommon(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) = {
MappedBankAccount
.find(By(MappedBankAccount.bank, bankId.value),
By(MappedBankAccount.theAccountId, accountId.value))
.map(
account =>
account
.mAccountRoutingScheme(APIUtil.ValueOrOBP(account.accountRoutingScheme))
.mAccountRoutingAddress(APIUtil.ValueOrOBPId(account.accountRoutingAddress,account.accountId.value))
).map(bankAccount => (bankAccount,callContext))
account
.mAccountRoutingScheme(APIUtil.ValueOrOBP(account.accountRoutingScheme))
.mAccountRoutingAddress(APIUtil.ValueOrOBPId(account.accountRoutingAddress, account.accountId.value))
).map(bankAccount => (bankAccount, callContext))
}
override def getBankAccountsFuture(bankIdAcountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Future[Box[List[BankAccount]]] = {
@ -397,7 +402,11 @@ object LocalMappedConnector extends Connector with MdcLoggable {
override def checkBankAccountExists(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) = {
getBankAccount(bankId: BankId, accountId: AccountId, callContext)
}
}
override def checkBankAccountExistsFuture(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]): Future[Box[(BankAccount, Option[CallContext])]] =
Future {
getBankAccount(bankId: BankId, accountId: AccountId, callContext)
}
override def getCoreBankAccounts(bankIdAcountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Box[(List[CoreAccount], Option[CallContext])]= {
Full(
@ -1666,6 +1675,9 @@ object LocalMappedConnector extends Connector with MdcLoggable {
override def getCounterparties(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): Box[(List[CounterpartyTrait], Option[CallContext])] = {
Counterparties.counterparties.vend.getCounterparties(thisBankId, thisAccountId, viewId).map(counterparties =>(counterparties, callContext))
}
override def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = Future {
(getCounterparties(thisBankId, thisAccountId, viewId, callContext) map (i => i._1), callContext)
}
override def createOrUpdateBank(
bankId: String,
@ -1797,6 +1809,10 @@ object LocalMappedConnector extends Connector with MdcLoggable {
)
}
def getCustomersByUserId(userId: String, callContext: Option[CallContext]): Box[(List[Customer], Option[CallContext])] = {
Full((Customer.customerProvider.vend.getCustomersByUserId(userId), callContext))
}
override def getCustomersByUserIdFuture(userId: String, callContext: Option[CallContext]): Future[Box[(List[Customer],Option[CallContext])]]=
Customer.customerProvider.vend.getCustomersByUserIdFuture(userId) map {
customersBox =>(customersBox.map(customers=>(customers,callContext)))
@ -1946,6 +1962,30 @@ object LocalMappedConnector extends Connector with MdcLoggable {
ProductAttribute.productAttributeProvider.vend.deleteProductAttribute(productAttributeId: String) map {
(_, callContext)
}
override def createAccountApplication(
productCode: ProductCode,
userId: Option[String],
customerId: Option[String],
callContext: Option[CallContext]
): OBPReturnType[Box[AccountApplication]] =
AccountApplication.accountApplication.vend.createAccountApplication(productCode, userId, customerId) map {
(_, callContext)
}
override def getAllAccountApplication(callContext: Option[CallContext]): OBPReturnType[Box[List[AccountApplication]]] =
AccountApplication.accountApplication.vend.getAll() map {
(_, callContext)
}
override def getAccountApplicationById(accountApplicationId: String, callContext: Option[CallContext]): OBPReturnType[Box[AccountApplication]] =
AccountApplication.accountApplication.vend.getById(accountApplicationId) map {
(_, callContext)
}
override def updateAccountApplicationStatus(accountApplicationId:String, status: String, callContext: Option[CallContext]): OBPReturnType[Box[AccountApplication]] =
AccountApplication.accountApplication.vend.updateStatus(accountApplicationId, status) map {
(_, callContext)
}
}

View File

@ -1,21 +1,14 @@
package code.bankconnectors
import java.text.SimpleDateFormat
import java.util.{Date, TimeZone, UUID}
import java.util.{Date, TimeZone}
import code.api.util.ErrorMessages._
import code.api.util.{APIUtil, CallContext}
import code.api.util.{APIUtil, CallContext, OBPQueryParam}
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
import code.bankconnectors.vMar2017.InboundAdapterInfoInternal
import code.branches.Branches.{Branch, BranchT}
import code.fx.{FXRate, fx}
import code.management.ImporterAPI.ImporterTransaction
import code.metadata.counterparties.{Counterparties, CounterpartyTrait, Metadata, MongoCounterparties}
import code.metadata.counterparties.{Counterparties, Metadata, MongoCounterparties}
import code.model._
import code.model.dataAccess._
import code.products.Products.{Product, ProductCode}
import code.transactionrequests.TransactionRequestTypeCharge
import code.transactionrequests.TransactionRequests._
import code.util.Helper
import code.util.Helper.MdcLoggable
import com.mongodb.QueryBuilder
@ -25,7 +18,6 @@ import net.liftweb.json.Extraction
import net.liftweb.json.JsonAST.JValue
import net.liftweb.mongodb.BsonDSL._
import net.liftweb.util.Helpers._
import net.liftweb.util.Props
import org.bson.types.ObjectId
import scala.concurrent.ExecutionContext.Implicits.global

View File

@ -8,7 +8,7 @@ import java.util.{Date, Locale, Optional, UUID}
import code.api.util.ErrorMessages._
import code.accountholder.{AccountHolders, MapperAccountHolders}
import code.api.util.{APIUtil, CallContext, ErrorMessages}
import code.api.util._
import code.api.v2_1_0.TransactionRequestCommonBodyJSON
import code.bankconnectors.vMar2017.InboundAdapterInfoInternal
import code.branches.Branches.{Branch, BranchT}

View File

@ -0,0 +1,426 @@
package code.bankconnectors.akka
import java.util.Date
import akka.pattern.ask
import code.actorsystem.ObpLookupSystem
import code.api.util.APIUtil.{AdapterImplementation, MessageDoc, OBPReturnType}
import code.api.util.ExampleValue._
import code.api.util._
import code.bankconnectors._
import code.bankconnectors.akka.InboundTransformerDec2018._
import code.bankconnectors.akka.actor.{AkkaConnectorActorInit, AkkaConnectorHelperActor}
import code.bankconnectors.vMar2017.InboundAdapterInfoInternal
import code.customer.{CreditLimit, CreditRating, Customer, CustomerFaceImage}
import code.metadata.counterparties.CounterpartyTrait
import code.model.{AccountId, AccountRouting, BankAccount, BankId, BankIdAccountId, CoreAccount, CounterpartyBespoke, Transaction, TransactionId, ViewId, Bank => BankTrait}
import com.sksamuel.avro4s.SchemaFor
import net.liftweb.common.{Box, Full}
import net.liftweb.json.Extraction.decompose
import net.liftweb.json.JsonAST.JValue
import net.liftweb.json.parse
import scala.collection.immutable.{List, Nil}
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
implicit override val nameOfConnector = AkkaConnector_vDec2018.toString
val messageFormat: String = "Dec2018"
implicit val formats = net.liftweb.json.DefaultFormats
override val messageDocs = ArrayBuffer[MessageDoc]()
val emptyObjectJson: JValue = decompose(Nil)
lazy val southSideActor = ObpLookupSystem.getAkkaConnectorActor(AkkaConnectorHelperActor.actorName)
messageDocs += MessageDoc(
process = "obp.get.AdapterInfo",
messageFormat = messageFormat,
description = "Gets information about the active general (non bank specific) Adapter that is responding to messages sent by OBP.",
outboundTopic = Some(OutboundGetAdapterInfo.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundAdapterInfo.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundGetAdapterInfo(
APIUtil.DateWithMsFormat.format(new Date()),
Examples.callContextAkka)
),
exampleInboundMessage = decompose(
InboundAdapterInfo(
name = "The south side of Akka connector",
version = messageFormat,
git_commit = gitCommitExample.value,
date = APIUtil.DateWithMsFormat.format(new Date()),
Examples.callContextAkka)
),
outboundAvroSchema = Some(parse(SchemaFor[OutboundGetAdapterInfo]().toString(true))),
inboundAvroSchema = Some(parse(SchemaFor[InboundAdapterInfo]().toString(true))),
adapterImplementation = Some(AdapterImplementation("- Core", 1))
)
override def getAdapterInfoFuture(callContext: Option[CallContext]): Future[Box[(InboundAdapterInfoInternal, Option[CallContext])]] = {
val req = OutboundGetAdapterInfo((new Date()).toString, callContext.map(_.toCallContextAkka))
val response = (southSideActor ? req).mapTo[InboundAdapterInfo]
response.map(r =>
Full(
(
InboundAdapterInfoInternal(
errorCode = "",
backendMessages = Nil,
name = r.name,
version = r.version,
git_commit = r.git_commit,
date = r.date
)
,
callContext
)
)
)
}
messageDocs += MessageDoc(
process = "obp.get.Banks",
messageFormat = messageFormat,
description = "Gets the banks list on this OBP installation.",
outboundTopic = Some(OutboundGetBanks.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundGetBanks.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundGetBanks(Examples.callContextAkka)
),
exampleInboundMessage = decompose(
InboundGetBanks(
Some(List(Examples.bank)),
Examples.callContextAkka)
),
outboundAvroSchema = Some(parse(SchemaFor[OutboundGetBanks]().toString(true))),
inboundAvroSchema = Some(parse(SchemaFor[InboundGetBanks]().toString(true))),
adapterImplementation = Some(AdapterImplementation("- Core", 2))
)
override def getBanksFuture(callContext: Option[CallContext]): Future[Box[(List[BankTrait], Option[CallContext])]] = {
val req = OutboundGetBanks(callContext.map(_.toCallContextAkka))
val response: Future[InboundGetBanks] = (southSideActor ? req).mapTo[InboundGetBanks]
response.map(_.payload.map(r => (r.map(toBank(_)), callContext)))
}
messageDocs += MessageDoc(
process = "obp.get.Bank",
messageFormat = messageFormat,
description = "Get a specific Bank as specified by bankId",
outboundTopic = Some(OutboundGetBank.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundGetBank.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundGetBank(
bankIdExample.value,
Examples.callContextAkka)
),
exampleInboundMessage = decompose(
InboundGetBank(
Some(Examples.bank),
Examples.callContextAkka)
),
outboundAvroSchema = Some(parse(SchemaFor[OutboundGetBank]().toString(true))),
inboundAvroSchema = Some(parse(SchemaFor[InboundGetBank]().toString(true))),
adapterImplementation = Some(AdapterImplementation("- Core", 5))
)
override def getBankFuture(bankId : BankId, callContext: Option[CallContext]): Future[Box[(BankTrait, Option[CallContext])]] = {
val req = OutboundGetBank(bankId.value, callContext.map(_.toCallContextAkka))
val response: Future[InboundGetBank] = (southSideActor ? req).mapTo[InboundGetBank]
response.map(_.payload.map(r => (toBank(r), callContext)))
}
messageDocs += MessageDoc(
process = "obp.check.BankAccountExists",
messageFormat = messageFormat,
description = "Check a bank Account exists - as specified by bankId and accountId.",
outboundTopic = Some(OutboundCheckBankAccountExists.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundCheckBankAccountExists.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundCheckBankAccountExists(
bankIdExample.value,
accountIdExample.value,
Examples.callContextAkka
)
),
exampleInboundMessage = decompose(
InboundCheckBankAccountExists(
Some(Examples.inboundAccountDec2018Example),
Examples.callContextAkka
)
),
adapterImplementation = Some(AdapterImplementation("Accounts", 4))
)
override def checkBankAccountExistsFuture(bankId : BankId, accountId : AccountId, callContext: Option[CallContext] = None): Future[Box[(BankAccount, Option[CallContext])]] = {
val req = OutboundCheckBankAccountExists(bankId.value, accountId.value, callContext.map(_.toCallContextAkka))
val response: Future[InboundCheckBankAccountExists] = (southSideActor ? req).mapTo[InboundCheckBankAccountExists]
response.map(_.payload.map(r => (toAccount(r), callContext)))
}
messageDocs += MessageDoc(
process = "obp.get.Account",
messageFormat = messageFormat,
description = "Get a single Account as specified by the bankId and accountId.",
outboundTopic = Some(OutboundGetAccount.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundGetAccount.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundGetAccount(
bankIdExample.value,
accountIdExample.value,
Examples.callContextAkka
)
),
exampleInboundMessage = decompose(
InboundGetAccount(
Some(Examples.inboundAccountDec2018Example),
Examples.callContextAkka
)
),
adapterImplementation = Some(AdapterImplementation("Accounts", 7))
)
override def getBankAccountFuture(bankId : BankId, accountId : AccountId, callContext: Option[CallContext]): OBPReturnType[Box[BankAccount]] = {
val req = OutboundGetAccount(bankId.value, accountId.value, callContext.map(_.toCallContextAkka))
val response = (southSideActor ? req).mapTo[InboundGetAccount]
response.map(a => (a.payload.map(toAccount(_)), callContext))
}
messageDocs += MessageDoc(
process = "obp.get.coreBankAccounts",
messageFormat = messageFormat,
description = "Get bank Accounts available to the User (without Metadata)",
outboundTopic = Some(OutboundGetCoreBankAccounts.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundGetCoreBankAccounts.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundGetCoreBankAccounts(
List(BankIdAccountId(BankId(bankIdExample.value), AccountId(accountIdExample.value))),
Examples.callContextAkka
)
),
exampleInboundMessage = decompose(
InboundGetCoreBankAccounts(
List(
InboundCoreAccount(
accountIdExample.value,
"My private account for Uber",
bankIdExample.value,
accountTypeExample.value,
List(AccountRouting(accountRoutingSchemeExample.value, accountRoutingAddressExample.value)
)
)
),
Examples.callContextAkka
)),
adapterImplementation = Some(AdapterImplementation("Accounts", 1))
)
override def getCoreBankAccountsFuture(BankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) : Future[Box[(List[CoreAccount], Option[CallContext])]] = {
val req = OutboundGetCoreBankAccounts(BankIdAccountIds, callContext.map(_.toCallContextAkka))
val response: Future[InboundGetCoreBankAccounts] = (southSideActor ? req).mapTo[InboundGetCoreBankAccounts]
response.map(a => Full(a.payload.map( x => CoreAccount(x.id,x.label,x.bankId,x.accountType, x.accountRoutings)), callContext))
}
messageDocs += MessageDoc(
process = "obp.get.CustomersByUserId",
messageFormat = messageFormat,
description = "Get Customers represented by the User.",
outboundTopic = Some(OutboundGetCustomersByUserId.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundGetCustomersByUserId.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundGetCustomersByUserId(
userIdExample.value,
Examples.callContextAkka
)
),
exampleInboundMessage = decompose(
InboundGetCustomersByUserId(
InboundCustomer(
customerId = customerIdExample.value,
bankId = bankIdExample.value,
number = customerNumberExample.value,
legalName = legalNameExample.value,
mobileNumber = "String",
email = "String",
faceImage = CustomerFaceImage(date = APIUtil.DateWithSecondsExampleObject, url = "String"),
dateOfBirth = APIUtil.DateWithSecondsExampleObject, relationshipStatus = "String",
dependents = 1,
dobOfDependents = List(APIUtil.DateWithSecondsExampleObject),
highestEducationAttained = "String",
employmentStatus = "String",
creditRating = CreditRating(rating = "String", source = "String"),
creditLimit = CreditLimit(currency = currencyExample.value, amount = creditLimitAmountExample.value),
kycStatus = false,
lastOkDate = APIUtil.DateWithSecondsExampleObject
) :: Nil,
Examples.callContextAkka
)
),
outboundAvroSchema = None,
inboundAvroSchema = None,
adapterImplementation = Some(AdapterImplementation("Accounts", 0))
)
override def getCustomersByUserIdFuture(userId: String , callContext: Option[CallContext]): Future[Box[(List[Customer], Option[CallContext])]] = {
val req = OutboundGetCustomersByUserId(userId, callContext.map(_.toCallContextAkka))
val response: Future[InboundGetCustomersByUserId] = (southSideActor ? req).mapTo[InboundGetCustomersByUserId]
response.map(a => Full(toCustomers(a.payload), callContext))
}
messageDocs += MessageDoc(
process = "obp.get.counterparties",
messageFormat = messageFormat,
description = "Get Counterparties available to the View on the Account specified by thisBankId, thisAccountId and viewId.",
outboundTopic = Some(OutboundGetCounterparties.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundGetCounterparties.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundGetCounterparties(
thisBankId = bankIdExample.value,
accountIdExample.value,
viewId = "Auditor",
Examples.callContextAkka
)
),
exampleInboundMessage = decompose(
InboundGetCounterparties(
InboundCounterparty(
createdByUserId = userIdExample.value,
name = "",
thisBankId = bankIdExample.value,
thisAccountId = accountIdExample.value,
thisViewId = "Auditor",
counterpartyId = counterpartyIdExample.value,
otherAccountRoutingScheme = accountRoutingSchemeExample.value,
otherAccountRoutingAddress = accountRoutingAddressExample.value,
otherBankRoutingScheme = bankRoutingSchemeExample.value,
otherBankRoutingAddress = bankRoutingAddressExample.value,
otherBranchRoutingScheme = accountRoutingSchemeExample.value,
otherBranchRoutingAddress = accountRoutingAddressExample.value,
isBeneficiary = true,
description = "",
otherAccountSecondaryRoutingScheme = accountRoutingSchemeExample.value,
otherAccountSecondaryRoutingAddress = accountRoutingAddressExample.value,
bespoke = List(
CounterpartyBespoke(key = "key", value = "value"))
) :: Nil,
Examples.callContextAkka
)
),
adapterImplementation = Some(AdapterImplementation("Payments", 0))
)
override def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = {
val req = OutboundGetCounterparties(thisBankId.value, thisAccountId.value, viewId.value, callContext.map(_.toCallContextAkka))
val response: Future[InboundGetCounterparties] = (southSideActor ? req).mapTo[InboundGetCounterparties]
response.map(a => (Full(a.payload), callContext))
}
messageDocs += MessageDoc(
process = "obp.get.Transactions",
messageFormat = messageFormat,
description = "Get Transactions for an Account specified by bankId and accountId. Pagination is achieved with limit, fromDate and toDate.",
outboundTopic = Some(OutboundGetTransactions.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundGetTransactions.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundGetTransactions(
bankId = bankIdExample.value,
accountId = accountIdExample.value,
limit = 100,
fromDate=APIUtil.DateWithSecondsExampleString,
toDate=APIUtil.DateWithSecondsExampleString,
Examples.callContextAkka
)
),
exampleInboundMessage = decompose(
InboundGetTransactions(
Nil,
Examples.callContextAkka
)
),
adapterImplementation = Some(AdapterImplementation("Transactions", 10))
)
override def getTransactionsFuture(bankId: BankId, accountId: AccountId, callContext: Option[CallContext], queryParams: OBPQueryParam*): OBPReturnType[Box[List[Transaction]]] = {
val limit = queryParams.collect { case OBPLimit(value) => value }.headOption.getOrElse(100)
val fromDate = queryParams.collect { case OBPFromDate(date) => APIUtil.DateWithMsFormat.format(date) }.headOption.getOrElse(APIUtil.DefaultFromDate.toString)
val toDate = queryParams.collect { case OBPToDate(date) => APIUtil.DateWithMsFormat.format(date) }.headOption.getOrElse(APIUtil.DefaultToDate.toString)
val req = OutboundGetTransactions(bankId.value, accountId.value, limit, fromDate, toDate, callContext.map(_.toCallContextAkka))
val response: Future[InboundGetTransactions] = (southSideActor ? req).mapTo[InboundGetTransactions]
response.map(a => (Full(toTransactions(a.payload)), callContext))
}
messageDocs += MessageDoc(
process = "obp.get.Transaction",
messageFormat = messageFormat,
description = "Get a single Transaction specified by bankId, accountId and transactionId",
outboundTopic = Some(OutboundGetTransaction.getClass.getSimpleName.replace("$", "")),
inboundTopic = Some(InboundGetTransaction.getClass.getSimpleName.replace("$", "")),
exampleOutboundMessage = decompose(
OutboundGetTransaction(
bankId = bankIdExample.value,
accountId = accountIdExample.value,
transactionId = transactionIdExample.value,
Examples.callContextAkka
)
),
exampleInboundMessage = decompose(
InboundGetTransaction(
None,
Examples.callContextAkka
)
),
adapterImplementation = Some(AdapterImplementation("Transactions", 11))
)
override def getTransactionFuture(bankId: BankId, accountId: AccountId, transactionId: TransactionId, callContext: Option[CallContext]): OBPReturnType[Box[Transaction]] = {
val req = OutboundGetTransaction(bankId.value, accountId.value, transactionId.value, callContext.map(_.toCallContextAkka))
val response: Future[InboundGetTransaction] = (southSideActor ? req).mapTo[InboundGetTransaction]
response.map(a => (a.payload.map(toTransaction(_)), callContext))
}
}
object Examples {
val inboundAccountDec2018Example =
InboundAccount(
bankId = bankIdExample.value,
branchId = branchIdExample.value,
accountId = accountIdExample.value,
accountNumber = accountNumberExample.value,
accountType = accountTypeExample.value,
balanceAmount = balanceAmountExample.value,
balanceCurrency = currencyExample.value,
owners = owner1Example.value :: owner1Example.value :: Nil,
viewsToGenerate = "Public" :: "Accountant" :: "Auditor" :: Nil,
bankRoutingScheme = bankRoutingSchemeExample.value,
bankRoutingAddress = bankRoutingAddressExample.value,
branchRoutingScheme = branchRoutingSchemeExample.value,
branchRoutingAddress = branchRoutingAddressExample.value,
accountRoutingScheme = accountRoutingSchemeExample.value,
accountRoutingAddress = accountRoutingAddressExample.value,
accountRouting = Nil,
accountRules = Nil
)
val callContextAkka = Some(
CallContextAkka(
Some(userIdExample.value),
Some("9ddb6507-9cec-4e5e-b09a-ef1cb203825a"),
correlationIdExample.value,
Some(sessionIdExample.value)
)
)
val bank =
InboundBank(
bankId = bankIdExample.value,
shortName = "The Royal Bank of Scotland",
fullName = "The Royal Bank of Scotland",
logoUrl = "http://www.red-bank-shoreditch.com/logo.gif",
websiteUrl = "http://www.red-bank-shoreditch.com",
bankRoutingScheme = "OBP",
bankRoutingAddress = "rbs"
)
}

View File

@ -0,0 +1,344 @@
package code.bankconnectors.akka
import java.lang
import java.util.Date
import code.api.util.{APIUtil, CallContextAkka}
import code.customer.{CreditLimit, CreditRating, Customer, CustomerFaceImage}
import code.metadata.counterparties.CounterpartyTrait
import code.model.dataAccess.MappedBankAccountData
import code.model.{AccountId, AccountRouting, AccountRule, BankAccount, BankId, BankIdAccountId, Counterparty, CounterpartyBespoke, Transaction, TransactionId, Bank => BankTrait}
import net.liftweb.mapper.By
import net.liftweb.util.Helpers.today
import scala.collection.immutable.List
import scala.math.BigDecimal
/**
*
* case classes used to define outbound Akka messages
*
*/
case class OutboundGetAdapterInfo(date: String,
callContext: Option[CallContextAkka])
case class OutboundGetBanks(callContext: Option[CallContextAkka])
case class OutboundGetBank(bankId: String,
callContext: Option[CallContextAkka])
case class OutboundCheckBankAccountExists(bankId: String,
ccountId: String,
callContext: Option[CallContextAkka])
case class OutboundGetAccount(bankId: String,
accountId: String,
callContext: Option[CallContextAkka])
case class OutboundGetCoreBankAccounts(bankIdAccountIds: List[BankIdAccountId],
callContext: Option[CallContextAkka])
case class OutboundGetCustomersByUserId(userId: String, callContext: Option[CallContextAkka])
case class OutboundGetCounterparties(thisBankId: String,
thisAccountId: String,
viewId: String,
callContext: Option[CallContextAkka])
case class OutboundGetTransactions(bankId: String,
accountId: String,
limit: Int,
fromDate: String,
toDate: String,
callContext: Option[CallContextAkka])
case class OutboundGetTransaction(bankId: String,
accountId: String,
transactionId: String,
callContext: Option[CallContextAkka])
/**
*
* case classes used to define inbound Akka messages
*
*/
case class InboundAdapterInfo(
name: String,
version: String,
git_commit: String,
date: String,
callContext: Option[CallContextAkka]
)
case class InboundGetBanks(payload: Option[List[InboundBank]],
callContext: Option[CallContextAkka])
case class InboundGetBank(payload: Option[InboundBank],
callContext: Option[CallContextAkka])
case class InboundCheckBankAccountExists(payload: Option[InboundAccount],
callContext: Option[CallContextAkka])
case class InboundGetAccount(payload: Option[InboundAccount],
callContext: Option[CallContextAkka])
case class InboundGetCoreBankAccounts(payload: List[InboundCoreAccount],
callContext: Option[CallContextAkka])
case class InboundGetCustomersByUserId(payload: List[InboundCustomer],
callContext: Option[CallContextAkka])
case class InboundGetCounterparties(payload: List[InboundCounterparty],
callContext: Option[CallContextAkka])
case class InboundGetTransactions(payload: List[InboundTransaction],
callContext: Option[CallContextAkka])
case class InboundGetTransaction(payload: Option[InboundTransaction],
callContext: Option[CallContextAkka])
case class InboundBank(
bankId: String,
shortName: String,
fullName: String,
logoUrl: String,
websiteUrl: String,
bankRoutingScheme: String,
bankRoutingAddress: String
)
case class InboundAccount(
bankId: String,
branchId: String,
accountId: String,
accountNumber: String,
accountType: String,
balanceAmount: String,
balanceCurrency: String,
owners: List[String],
viewsToGenerate: List[String],
bankRoutingScheme: String,
bankRoutingAddress: String,
branchRoutingScheme: String,
branchRoutingAddress: String,
accountRoutingScheme: String,
accountRoutingAddress: String,
accountRouting: List[AccountRouting],
accountRules: List[AccountRule]
)
case class InboundCoreAccount(
id: String,
label: String,
bankId: String,
accountType: String,
accountRoutings: List[AccountRouting]
)
case class InboundCustomer(
customerId: String,
bankId: String,
number: String,
legalName: String,
mobileNumber: String,
email: String,
faceImage: CustomerFaceImage,
dateOfBirth: Date,
relationshipStatus: String,
dependents: Integer,
dobOfDependents: List[Date],
highestEducationAttained: String,
employmentStatus: String,
creditRating: CreditRating,
creditLimit: CreditLimit,
kycStatus: lang.Boolean,
lastOkDate: Date
)
case class InboundTransaction(
uuid: String,
id: TransactionId,
thisAccount: BankAccount,
otherAccount: Counterparty,
transactionType: String,
amount: BigDecimal,
currency: String,
description: Option[String],
startDate: Date,
finishDate: Date,
balance: BigDecimal
)
case class InboundCounterparty(
createdByUserId: String,
name: String,
thisBankId: String,
thisAccountId: String,
thisViewId: String,
counterpartyId: String,
otherAccountRoutingScheme: String,
otherAccountRoutingAddress: String,
otherBankRoutingScheme: String,
otherBankRoutingAddress: String,
otherBranchRoutingScheme: String,
otherBranchRoutingAddress: String,
isBeneficiary: Boolean,
description: String,
otherAccountSecondaryRoutingScheme: String,
otherAccountSecondaryRoutingAddress: String,
bespoke: List[CounterpartyBespoke]) extends CounterpartyTrait
object InboundTransformerDec2018 {
/*
* Customer
*/
case class AkkaDec2018Customer(
customerId: String,
bankId: String,
number: String,
legalName: String,
mobileNumber: String,
email: String,
faceImage: CustomerFaceImage,
dateOfBirth: Date,
relationshipStatus: String,
dependents: Integer,
dobOfDependents: List[Date],
highestEducationAttained: String,
employmentStatus: String,
creditRating: CreditRating,
creditLimit: CreditLimit,
kycStatus: lang.Boolean,
lastOkDate: Date,
title: String = "", //These new fields for V310, not from Connector for now.
branchId: String = "", //These new fields for V310, not from Connector for now.
nameSuffix: String = "", //These new fields for V310, not from Connector for now.
) extends Customer
def toCustomer(customer: InboundCustomer): Customer = {
AkkaDec2018Customer(
customerId = customer.customerId,
bankId = customer.bankId,
number = customer.number,
legalName = customer.legalName,
mobileNumber = customer.mobileNumber,
email = customer.email,
faceImage = customer.faceImage,
dateOfBirth = customer.dateOfBirth,
relationshipStatus = customer.relationshipStatus,
dependents = customer.dependents,
dobOfDependents = customer.dobOfDependents,
highestEducationAttained = customer.highestEducationAttained,
employmentStatus = customer.employmentStatus,
creditRating = customer.creditRating,
creditLimit = customer.creditLimit,
kycStatus = customer.kycStatus,
lastOkDate = customer.lastOkDate,
)
}
def toCustomers(customers: List[InboundCustomer]): List[Customer] = {
customers.map(toCustomer)
}
/*
* Transaction
*/
def toTransaction(t: InboundTransaction): Transaction = {
new Transaction(
uuid = t.uuid,
id = t.id,
thisAccount = t.thisAccount,
otherAccount = t.otherAccount,
transactionType = t.transactionType,
amount = t.amount,
currency = t.currency,
description = t.description,
startDate = t.startDate,
finishDate = t.finishDate,
balance = t.balance
)
}
def toTransactions(t: List[InboundTransaction]): List[Transaction] = {
t.map(toTransaction)
}
/*
* Bank
*/
case class BankAkka(b: InboundBank) extends BankTrait {
override def bankId = BankId(b.bankId)
override def fullName = b.fullName
override def shortName = b.shortName
override def logoUrl = b.logoUrl
override def websiteUrl = b.websiteUrl
override def bankRoutingScheme = b.bankRoutingScheme
override def bankRoutingAddress = b.bankRoutingAddress
override def swiftBic = ""
override def nationalIdentifier: String = ""
}
def toBank(b: InboundBank): BankTrait = BankAkka(b)
/*
* Account
*/
case class BankAccountDec2018(a: InboundAccount) extends BankAccount {
override def accountId: AccountId = AccountId(a.accountId)
override def accountType: String = a.accountType
override def balance: BigDecimal = BigDecimal(a.balanceAmount)
override def currency: String = a.balanceCurrency
override def name: String = a.owners.head
override def swift_bic: Option[String] = Some("swift_bic")
override def iban: Option[String] = Some("iban")
override def number: String = a.accountNumber
override def bankId: BankId = BankId(a.bankId)
override def lastUpdate: Date = APIUtil.DateWithMsFormat.parse(today.getTime.toString)
override def accountHolder: String = a.owners.head
override
def label: String = (for {
d <- MappedBankAccountData.find(By(MappedBankAccountData.accountId, a.accountId))
} yield {
d.getLabel
}).getOrElse(a.accountNumber)
override def accountRoutingScheme: String = a.accountRoutingScheme
override def accountRoutingAddress: String = a.accountRoutingAddress
override def accountRoutings: List[AccountRouting] = List()
override def branchId: String = a.branchId
override def accountRules: List[AccountRule] = a.accountRules
}
def toAccount(account: InboundAccount): BankAccount = BankAccountDec2018(account)
}

View File

@ -0,0 +1,109 @@
package code.bankconnectors.akka.actor
import code.api.util.APIUtil
import code.util.Helper
object AkkaConnectorActorConfig {
val remoteHostname = APIUtil.getPropsValue("akka_connector.hostname").openOr("127.0.0.1")
val remotePort = APIUtil.getPropsValue("akka_connector.port").openOr("2662")
val localHostname = "127.0.0.1"
val localPort = Helper.findAvailablePort()
val akka_loglevel = APIUtil.getPropsValue("akka_connector.loglevel").openOr("INFO")
val commonConf =
"""
akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = """ + akka_loglevel + """
actor {
provider = "akka.remote.RemoteActorRefProvider"
allow-java-serialization = off
kryo {
type = "graph"
idstrategy = "default"
buffer-size = 65536
max-buffer-size = -1
use-manifests = false
use-unsafe = true
post-serialization-transformations = "off"
#post-serialization-transformations = "lz4,aes"
#encryption {
# aes {
# mode = "AES/CBC/PKCS5Padding"
# key = j68KkRjq21ykRGAQ
# IV-length = 16
# }
#}
implicit-registration-logging = false
kryo-trace = false
resolve-subclasses = true
}
serializers {
kryo = "com.twitter.chill.akka.AkkaSerializer"
}
serialization-bindings {
"net.liftweb.common.Full" = kryo,
"net.liftweb.common.Empty" = kryo,
"net.liftweb.common.Box" = kryo,
"net.liftweb.common.ParamFailure" = kryo,
"code.api.APIFailure" = kryo,
"code.model.BankAccount" = kryo,
"code.model.View" = kryo,
"code.model.dataAccess.ViewImpl" = kryo,
"code.model.User" = kryo,
"code.model.ViewId" = kryo,
"code.model.ViewIdBankIdAccountId" = kryo,
"code.model.Permission" = kryo,
"scala.Unit" = kryo,
"scala.Boolean" = kryo,
"java.io.Serializable" = kryo,
"scala.collection.immutable.List" = kryo,
"akka.actor.ActorSelectionMessage" = kryo,
"code.model.Consumer" = kryo,
"code.model.AppType" = kryo
}
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty {
tcp {
send-buffer-size = 50000000
receive-buffer-size = 50000000
maximum-frame-size = 52428800
}
}
}
}
"""
val lookupConf =
s"""
${commonConf}
akka {
remote.netty.tcp.hostname = ${localHostname}
remote.netty.tcp.port = 0
}
"""
val localConf =
s"""
${commonConf}
akka {
remote.netty.tcp.hostname = ${localHostname}
remote.netty.tcp.port = ${localPort}
}
"""
val remoteConf =
s"""
${commonConf}
akka {
remote.netty.tcp.hostname = ${remoteHostname}
remote.netty.tcp.port = ${remotePort}
}
"""
}

View File

@ -0,0 +1,13 @@
package code.bankconnectors.akka.actor
import akka.util.Timeout
import code.api.util.APIUtil
import code.util.Helper.MdcLoggable
import scala.concurrent.duration._
trait AkkaConnectorActorInit extends MdcLoggable{
// Default is 3 seconds, which should be more than enough for slower systems
val ACTOR_TIMEOUT: Long = APIUtil.getPropsAsLongValue("akka_connector.timeout").openOr(3)
implicit val timeout = Timeout(ACTOR_TIMEOUT * (1000 milliseconds))
}

View File

@ -0,0 +1,20 @@
package code.bankconnectors.akka.actor
import akka.actor.{ActorSystem, Props}
import code.api.util.APIUtil
import code.util.Helper.MdcLoggable
object AkkaConnectorHelperActor extends MdcLoggable {
val actorName = APIUtil.getPropsValue("akka_connector.name_of_actor", "akka-connector-actor")
//This method is called in Boot.scala
def startAkkaConnectorHelperActors(actorSystem: ActorSystem): Unit = {
logger.info("***** Starting " + actorName + " at the North side *****")
val actorsHelper = Map(
Props[SouthSideActorOfAkkaConnector] -> actorName
)
actorsHelper.foreach { a => logger.info(actorSystem.actorOf(a._1, name = a._2)) }
}
}

View File

@ -0,0 +1,188 @@
package code.bankconnectors.akka.actor
import java.util.Date
import akka.actor.{Actor, ActorLogging}
import code.api.util.{APIUtil, OBPFromDate, OBPLimit, OBPToDate}
import code.bankconnectors.LocalMappedConnector._
import code.bankconnectors.akka._
import code.customer.{CreditLimit, CreditRating, Customer, CustomerFaceImage}
import code.metadata.counterparties.CounterpartyTrait
import code.model.dataAccess.MappedBank
import code.model.{Bank => _, _}
import code.util.Helper.MdcLoggable
import net.liftweb.common.Box
import scala.collection.immutable.List
/**
* This Actor acts in next way:
*/
class SouthSideActorOfAkkaConnector extends Actor with ActorLogging with MdcLoggable {
def receive: Receive = waitingForRequest
private def waitingForRequest: Receive = {
case OutboundGetAdapterInfo(_, cc) =>
val result =
InboundAdapterInfo(
"The south side of Akka connector",
"Dec2018",
APIUtil.gitCommit,
APIUtil.DateWithMsFormat.format(new Date()),
cc
)
sender ! result
case OutboundGetBanks(cc) =>
val result: Box[List[MappedBank]] = getBanks(None).map(r => r._1)
sender ! InboundGetBanks(result.map(l => l.map(Transformer.bank(_))).toOption, cc)
case OutboundGetBank(bankId, cc) =>
val result: Box[MappedBank] = getBank(BankId(bankId), None).map(r => r._1)
sender ! InboundGetBank(result.map(Transformer.bank(_)).toOption, cc)
case OutboundCheckBankAccountExists(bankId, accountId, cc) =>
val result: Box[BankAccount] = checkBankAccountExists(BankId(bankId), AccountId(accountId), None).map(r => r._1)
sender ! InboundCheckBankAccountExists(result.map(Transformer.bankAccount(_)).toOption, cc)
case OutboundGetAccount(bankId, accountId, cc) =>
val result: Box[BankAccount] = getBankAccount(BankId(bankId), AccountId(accountId), None).map(r => r._1)
org.scalameta.logger.elem(result)
sender ! InboundGetAccount(result.map(Transformer.bankAccount(_)).toOption, cc)
case OutboundGetCoreBankAccounts(bankIdAccountIds, cc) =>
val result: Box[List[CoreAccount]] = getCoreBankAccounts(bankIdAccountIds, None).map(r => r._1)
sender ! InboundGetCoreBankAccounts(result.getOrElse(Nil).map(Transformer.coreAccount(_)), cc)
case OutboundGetCustomersByUserId(userId, cc) =>
val result: Box[List[Customer]] = getCustomersByUserId(userId, None).map(r => r._1)
sender ! InboundGetCustomersByUserId(result.getOrElse(Nil).map(Transformer.toInternalCustomer(_)), cc)
case OutboundGetCounterparties(thisBankId, thisAccountId, viewId, cc) =>
val result: Box[List[CounterpartyTrait]] = getCounterparties(BankId(thisBankId), AccountId(thisAccountId), ViewId(viewId), None).map(r => r._1)
sender ! InboundGetCounterparties(result.getOrElse(Nil).map(Transformer.toInternalCounterparty(_)), cc)
case OutboundGetTransactions(bankId, accountId, limit, fromDate, toDate, cc) =>
val from = APIUtil.DateWithMsFormat.parse(fromDate)
val to = APIUtil.DateWithMsFormat.parse(toDate)
val result = getTransactions(BankId(bankId), AccountId(accountId), None, List(OBPLimit(limit), OBPFromDate(from), OBPToDate(to)): _*).map(r => r._1)
sender ! InboundGetTransactions(result.getOrElse(Nil).map(Transformer.toInternalTransaction(_)), cc)
case OutboundGetTransaction(bankId, accountId, transactionId, cc) =>
val result = getTransaction(BankId(bankId), AccountId(accountId), TransactionId(transactionId), None).map(r => r._1)
sender ! InboundGetTransaction(result.map(Transformer.toInternalTransaction(_)), cc)
case message =>
logger.warn("[AKKA ACTOR ERROR - REQUEST NOT RECOGNIZED] " + message)
}
}
object Transformer {
def bank(mb: MappedBank): InboundBank =
InboundBank(
bankId=mb.bankId.value,
shortName=mb.shortName,
fullName=mb.fullName,
logoUrl=mb.logoUrl,
websiteUrl=mb.websiteUrl,
bankRoutingScheme=mb.bankRoutingScheme,
bankRoutingAddress=mb.bankRoutingAddress
)
def bankAccount(acc: BankAccount) =
InboundAccount(
bankId = acc.bankId.value,
branchId = acc.branchId,
accountId = acc.accountId.value,
accountNumber = acc.number,
accountType = acc.accountType,
balanceAmount = acc.balance.toString(),
balanceCurrency = acc.currency,
owners = acc.customerOwners.map(_.customerId).toList,
viewsToGenerate = Nil,
bankRoutingScheme = acc.bankRoutingScheme,
bankRoutingAddress = acc.bankRoutingAddress,
branchRoutingScheme = "",
branchRoutingAddress = "",
accountRoutingScheme = acc.accountRoutingScheme,
accountRoutingAddress = acc.accountRoutingAddress,
accountRouting = Nil,
accountRules = Nil
)
def coreAccount(a: CoreAccount) =
InboundCoreAccount(
id = a.id,
label = a.label,
bankId = a.bankId,
accountType = a.accountType,
accountRoutings = a.accountRoutings
)
def toInternalCustomer(customer: Customer): InboundCustomer = {
InboundCustomer(
customerId = customer.customerId,
bankId = customer.bankId,
number = customer.number,
legalName = customer.legalName,
mobileNumber = customer.mobileNumber,
email = customer.email,
faceImage = CustomerFaceImage(customer.faceImage.date,customer.faceImage.url),
dateOfBirth = customer.dateOfBirth,
relationshipStatus = customer.relationshipStatus,
dependents = customer.dependents,
dobOfDependents = customer.dobOfDependents,
highestEducationAttained = customer.highestEducationAttained,
employmentStatus = customer.employmentStatus,
creditRating = CreditRating(customer.creditRating.rating, customer.creditRating.source),
creditLimit = CreditLimit(customer.creditLimit.amount,customer.creditLimit.currency),
kycStatus = customer.kycStatus,
lastOkDate = customer.lastOkDate,
)
}
def toInternalCounterparty(c: CounterpartyTrait) = {
InboundCounterparty(
createdByUserId=c.createdByUserId,
name=c.name,
thisBankId=c.thisBankId,
thisAccountId=c.thisAccountId,
thisViewId=c.thisViewId,
counterpartyId=c.counterpartyId,
otherAccountRoutingScheme=c.otherAccountRoutingScheme,
otherAccountRoutingAddress=c.otherAccountRoutingAddress,
otherBankRoutingScheme=c.otherBankRoutingScheme,
otherBankRoutingAddress=c.otherBankRoutingAddress,
otherBranchRoutingScheme=c.otherBankRoutingScheme,
otherBranchRoutingAddress=c.otherBranchRoutingAddress,
isBeneficiary=c.isBeneficiary,
description=c.description,
otherAccountSecondaryRoutingScheme=c.otherAccountSecondaryRoutingScheme,
otherAccountSecondaryRoutingAddress=c.otherAccountSecondaryRoutingAddress,
bespoke=c.bespoke
)
}
def toInternalTransaction(t: Transaction): InboundTransaction = {
InboundTransaction(
uuid = t.uuid ,
id = t.id ,
thisAccount = t.thisAccount ,
otherAccount = t.otherAccount ,
transactionType = t.transactionType ,
amount = t.amount ,
currency = t.currency ,
description = t.description ,
startDate = t.startDate ,
finishDate = t.finishDate ,
balance = t.balance
)
}
}

View File

@ -31,7 +31,7 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
import code.api.cache.Caching
import code.api.util.APIUtil.{MessageDoc, getSecondsCache, saveConnectorMetric}
import code.api.util.ErrorMessages._
import code.api.util.{APIUtil, ApiSession, CallContext, ErrorMessages}
import code.api.util._
import code.api.util.APIUtil._
import code.api.v3_1_0.{AccountV310Json, CardObjectJson, CheckbookOrdersJson}
import code.atms.Atms.{AtmId, AtmT}
@ -50,6 +50,7 @@ import com.google.common.cache.CacheBuilder
import com.sksamuel.avro4s.SchemaFor
import com.tesobe.{CacheKeyFromArguments, CacheKeyOmit}
import net.liftweb.common.{Box, _}
import net.liftweb.json
import net.liftweb.json.Extraction._
import net.liftweb.json.JsonAST.JValue
import net.liftweb.json.{Extraction, MappingException, parse}
@ -165,6 +166,47 @@ trait KafkaMappedConnector_vJune2017 extends Connector with KafkaHelper with Mdc
res
}
override def getAdapterInfoFuture(callContext: Option[CallContext]): Future[Box[(InboundAdapterInfoInternal, Option[CallContext])]] = {
val req = OutboundGetAdapterInfo(
AuthInfo(sessionId = callContext.get.correlationId),
DateWithSecondsExampleString
)
logger.debug(s"Kafka getAdapterInfoFuture Req says: is: $req")
val future = for {
res <- processToFuture[OutboundGetAdapterInfo](req) map {
f =>
try {
f.extract[InboundAdapterInfo]
} catch {
case e: Exception =>
val received = json.compactRender(f)
val expected = SchemaFor[InboundAdapterInfo]().toString(false)
val err = s"Extraction Failed: You received this ($received). We expected this ($expected)"
sendOutboundAdapterError(err)
throw new MappingException(err, e)
}
} map {
x => x.data
}
} yield {
Full(res)
}
val res = future map {
case Full(list) if (list.errorCode=="") =>
Full(list, callContext)
case Full(list) if (list.errorCode!="") =>
Failure("INTERNAL-"+ list.errorCode+". + CoreBank-Status:"+ list.backendMessages)
case _ =>
Failure(ErrorMessages.UnknownError)
}
logger.debug(s"Kafka getAdapterInfoFuture says res is $res")
res
}
messageDocs += MessageDoc(
process = "obp.get.Banks",
@ -645,6 +687,11 @@ trait KafkaMappedConnector_vJune2017 extends Connector with KafkaHelper with Mdc
}
}
}("getBankAccount")
override def checkBankAccountExistsFuture(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) =
Future {
checkBankAccountExists(bankId, accountId, callContext)
}
messageDocs += MessageDoc(
process = "obp.get.coreBankAccounts",
@ -1327,14 +1374,14 @@ trait KafkaMappedConnector_vJune2017 extends Connector with KafkaHelper with Mdc
val res = box match {
case Full((data, status, callContext)) if (status.errorCode=="") =>
//For consistency with sandbox mode, we need combine obp transactions in database and adapter transactions
val transancitonRequests = for{
val transactionRequests = for{
adapterTransactionRequests <- Full(data)
//TODO, this will cause performance issue, we need limit the number of transaction requests.
obpTransactionRequests <- LocalMappedConnector.getTransactionRequestsImpl210(fromAccount) ?~! s"$ConnectorEmptyResponse, error on LocalMappedConnector.getTransactionRequestsImpl210"
} yield {
adapterTransactionRequests ::: obpTransactionRequests
}
transancitonRequests.map(transactionRequests =>(transactionRequests, callContext))
transactionRequests.map(transactionRequests =>(transactionRequests, callContext))
case Full((data, status, _)) if (status.errorCode!="") =>
Failure("INTERNAL-"+ status.errorCode+". + CoreBank-Status:"+ status.backendMessages)
case Empty =>
@ -1434,6 +1481,9 @@ trait KafkaMappedConnector_vJune2017 extends Connector with KafkaHelper with Mdc
}
}
}("getCounterparties")
override def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = Future {
(getCounterparties(thisBankId, thisAccountId, viewId, callContext) map (i => i._1), callContext)
}
messageDocs += MessageDoc(
process = "obp.get.CounterpartyByCounterpartyId",

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.bankconnectors.vMar2017
import java.util.Date

View File

@ -29,7 +29,7 @@ import java.util.{Date, Locale, UUID}
import code.api.util.ErrorMessages._
import code.accountholder.AccountHolders
import code.api.util.APIUtil.MessageDoc
import code.api.util.{APIUtil, CallContext, ErrorMessages}
import code.api.util._
import code.api.v2_1_0._
import code.bankconnectors._
import code.branches.Branches.{Branch, BranchT}

View File

@ -25,12 +25,13 @@ Berlin 13359, Germany
import java.text.SimpleDateFormat
import java.util.UUID.randomUUID
import code.api.JSONFactoryGateway.PayloadOfJwtJSON
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
import code.api.cache.Caching
import code.api.util.APIUtil.{MessageDoc, saveConnectorMetric, _}
import code.api.util.ErrorMessages._
import code.api.util.{APIUtil, CallContext, ErrorMessages}
import code.api.util._
import code.api.v3_1_0.CardObjectJson
import code.atms.Atms.AtmId
import code.bankconnectors._
@ -52,6 +53,7 @@ import net.liftweb.json.Extraction._
import net.liftweb.json.JsonAST.JValue
import net.liftweb.json.{Extraction, MappingException, parse}
import net.liftweb.util.Helpers.{now, tryo}
import scala.collection.immutable.{List, Nil}
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
@ -62,6 +64,7 @@ import code.api.util.ExampleValue._
import code.api.v1_2_1.AmountOfMoneyJsonV121
import code.api.v2_1_0.{TransactionRequestBodyCommonJSON, TransactionRequestCommonBodyJSON}
import code.context.UserAuthContextProvider
import code.metadata.counterparties.CounterpartyTrait
import code.users.Users
import net.liftweb
import net.liftweb.json
@ -288,6 +291,42 @@ trait KafkaMappedConnector_vSept2018 extends Connector with KafkaHelper with Mdc
res
}
override def getAdapterInfoFuture(callContext: Option[CallContext]): Future[Box[(InboundAdapterInfoInternal, Option[CallContext])]] = {
val req = OutboundGetAdapterInfo(DateWithSecondsExampleString)
logger.debug(s"Kafka getAdapterInfoFuture Req says: is: $req")
val future = for {
res <- processToFuture[OutboundGetAdapterInfo](req) map {
f =>
try {
f.extract[InboundAdapterInfo]
} catch {
case e: Exception =>
val received = liftweb.json.compactRender(f)
val expected = SchemaFor[InboundAdapterInfo]().toString(false)
val err = s"Extraction Failed: You received this ($received). We expected this ($expected)"
sendOutboundAdapterError(err)
throw new MappingException(err, e)
}
} map {
x => x.data
}
} yield {
Full(res)
}
val res = future map {
case Full(list) if (list.errorCode=="") =>
Full(list, callContext)
case Full(list) if (list.errorCode!="") =>
Failure("INTERNAL-"+ list.errorCode+". + CoreBank-Status:"+ list.backendMessages)
case _ =>
Failure(ErrorMessages.UnknownError)
}
logger.debug(s"Kafka getAdapterInfoFuture says res is $res")
res
}
messageDocs += MessageDoc(
process = "obp.get.User",
@ -871,6 +910,11 @@ trait KafkaMappedConnector_vSept2018 extends Connector with KafkaHelper with Mdc
}
}
}("getBankAccount")
override def checkBankAccountExistsFuture(bankId: BankId, accountId: AccountId, callContext: Option[CallContext]) =
Future {
checkBankAccountExists(bankId, accountId, callContext)
}
messageDocs += MessageDoc(
process = "obp.get.coreBankAccounts",
@ -1653,6 +1697,9 @@ trait KafkaMappedConnector_vSept2018 extends Connector with KafkaHelper with Mdc
}
}
}("getCounterparties")
override def getCounterpartiesFuture(thisBankId: BankId, thisAccountId: AccountId, viewId: ViewId, callContext: Option[CallContext] = None): OBPReturnType[Box[List[CounterpartyTrait]]] = Future {
(getCounterparties(thisBankId, thisAccountId, viewId, callContext) map (i => i._1), callContext)
}
messageDocs += MessageDoc(
process = "obp.get.CounterpartyByCounterpartyId",

View File

@ -4,11 +4,11 @@ package code.branches
/* For branches */
// Need to import these one by one because in same package!
import code.bankconnectors.OBPQueryParam
import code.branches.Branches.{Branch, BranchId, BranchT}
import code.api.util.OBPQueryParam
import code.branches.Branches.{BranchId, BranchT}
import code.common._
import code.model.BankId
import net.liftweb.common.{Box, Logger}
import net.liftweb.common.Logger
import net.liftweb.util.SimpleInjector
object Branches extends SimpleInjector {
@ -232,9 +232,6 @@ object Branches extends SimpleInjector {
)
import code.common.Routing

View File

@ -1,12 +1,12 @@
package code.branches
import code.bankconnectors._
import code.api.util.{OBPLimit, OBPOffset, OBPQueryParam}
import code.branches.Branches._
import code.common._
import code.model.BankId
import code.util.{TwentyFourHourClockString, UUIDString}
import net.liftweb.common.Logger
import net.liftweb.mapper.{By, _}
import net.liftweb.common.{Box, Logger}
object MappedBranchesProvider extends BranchesProvider {

View File

@ -34,6 +34,7 @@ trait ConsumersProvider {
def updateConsumer(id: Long, key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String]): Box[Consumer]
def updateConsumerCallLimits(id: Long, perMinute: Option[String], perHour: Option[String], perDay: Option[String], perWeek: Option[String], perMonth: Option[String]): Future[Box[Consumer]]
def getOrCreateConsumer(consumerId: Option[String], key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String]): Box[Consumer]
def populateMissingUUIDs(): Boolean
}
@ -50,6 +51,7 @@ class RemotedataConsumersCaseClasses {
case class updateConsumer(id: Long, key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String])
case class updateConsumerCallLimits(id: Long, perMinute: Option[String], perHour: Option[String], perDay: Option[String], perWeek: Option[String], perMonth: Option[String])
case class getOrCreateConsumer(consumerId: Option[String], key: Option[String], secret: Option[String], isActive: Option[Boolean], name: Option[String], appType: Option[AppType], description: Option[String], developerEmail: Option[String], redirectURL: Option[String], createdByUserId: Option[String])
case class populateMissingUUIDs()
}
object RemotedataConsumersCaseClasses extends RemotedataConsumersCaseClasses

View File

@ -3,12 +3,11 @@ package code.customer
import java.lang
import java.util.Date
import code.api.util.APIUtil
import code.bankconnectors.OBPQueryParam
import code.api.util.{APIUtil, OBPQueryParam}
import code.model.{BankId, User}
import code.remotedata.RemotedataCustomers
import net.liftweb.common.Box
import net.liftweb.util.{Props, SimpleInjector}
import net.liftweb.util.SimpleInjector
import scala.collection.immutable.List
import scala.concurrent.Future

View File

@ -3,10 +3,9 @@ package code.customer
import java.lang
import java.util.Date
import code.api.util.APIUtil
import code.bankconnectors._
import code.api.util._
import code.model.{BankId, User}
import code.usercustomerlinks.{MappedUserCustomerLink, MappedUserCustomerLinkProvider, UserCustomerLink}
import code.usercustomerlinks.{MappedUserCustomerLinkProvider, UserCustomerLink}
import code.users.Users
import code.util.Helper.MdcLoggable
import code.util.{MappedUUID, UUIDString}
@ -16,8 +15,8 @@ import net.liftweb.mapper.{By, _}
import net.liftweb.util.Helpers.tryo
import scala.collection.immutable.List
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object MappedCustomerProvider extends CustomerProvider with MdcLoggable {
@ -186,7 +185,7 @@ object MappedCustomerProvider extends CustomerProvider with MdcLoggable {
for {
customer <- MappedCustomer.findAll(NullRef(MappedCustomer.mCustomerId))
} yield {
customer.mTitle(APIUtil.generateUUID()).save()
customer.mCustomerId(APIUtil.generateUUID()).save()
}
}.forall(_ == true)

View File

@ -35,13 +35,13 @@ trait KafkaHelper extends ObpActorInit with MdcLoggable {
process(mapRequest)
}
def process (request: Map[String, String]): JValue ={
extractFuture(actor ? request)
}
def process (request: Map[String, String]): JValue = getValueFromFuture(
(actor ? request).mapTo[JValue]
)
def processToBox[T](request: T): Box[JValue] = {
extractFutureToBox(actor ? request)
}
def processToBox[T](request: T): Box[JValue] = getValueFromFuture(
(actor ? request).mapTo[Box[JValue]]
)
def processToFuture[T](request: T): Future[JValue] = {
(actor ? request).mapTo[JValue]

View File

@ -45,7 +45,7 @@ object NorthSideConsumer {
Map[String, String](
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> brokers,
ConsumerConfig.GROUP_ID_CONFIG -> group,
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> KafkaConsumer.autoOffsetResetConfig,
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> OBPKafkaConsumer.autoOffsetResetConfig,
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> keyDeserealizer,
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> valueDeserealizer,
"security.protocol" -> "SSL",
@ -58,7 +58,7 @@ object NorthSideConsumer {
Map[String, String](
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> brokers,
ConsumerConfig.GROUP_ID_CONFIG -> group,
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> KafkaConsumer.autoOffsetResetConfig,
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> OBPKafkaConsumer.autoOffsetResetConfig,
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> keyDeserealizer,
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> valueDeserealizer
)

View File

@ -1,5 +1,5 @@
package code.kafka
object KafkaConsumer extends KafkaConfig {
object OBPKafkaConsumer extends KafkaConfig {
lazy val primaryConsumer = NorthSideConsumer(bootstrapServers, "", groupId + "-north.side.consumer", new NorthSideConsumerMessageProcessor())
}

View File

@ -2,11 +2,10 @@ package code.metrics
import java.util.{Calendar, Date}
import code.api.util.APIUtil
import code.bankconnectors.OBPQueryParam
import code.api.util.{APIUtil, OBPQueryParam}
import code.remotedata.RemotedataMetrics
import net.liftweb.common.Box
import net.liftweb.util.{Props, SimpleInjector}
import net.liftweb.util.SimpleInjector
import scala.concurrent.Future

View File

@ -2,8 +2,8 @@ package code.metrics
import java.util.Date
import code.bankconnectors._
import code.util.{MappedUUID}
import code.api.util._
import code.util.MappedUUID
import net.liftweb.mapper._
object ConnectorMetrics extends ConnectorMetricsProvider {

View File

@ -2,10 +2,9 @@ package code.metrics
import java.util.{Calendar, Date}
import code.api.util.APIUtil
import code.bankconnectors.OBPQueryParam
import code.api.util.{APIUtil, OBPQueryParam}
import code.remotedata.RemotedataConnectorMetrics
import net.liftweb.util.{Props, SimpleInjector}
import net.liftweb.util.SimpleInjector
object ConnectorMetricsProvider extends SimpleInjector {

View File

@ -2,12 +2,10 @@ package code.metrics
import java.util.Date
import code.api.util.APIUtil
import code.bankconnectors._
import code.api.util._
import code.search.elasticsearchMetrics
import net.liftweb.common.Box
import net.liftweb.mapper._
import net.liftweb.util.Props
import scala.concurrent.Future

View File

@ -1,19 +1,18 @@
package code.metrics
import java.sql.{PreparedStatement, Time, Timestamp}
import java.sql.{PreparedStatement, Timestamp}
import java.util.Date
import code.api.util.APIUtil
import code.api.util.ErrorMessages._
import code.bankconnectors.{OBPImplementedByPartialFunction, _}
import code.api.util._
import code.util.Helper.MdcLoggable
import code.util.{MappedUUID, UUIDString}
import net.liftweb.common.{Box, Empty, Full}
import net.liftweb.common.{Box, Full}
import net.liftweb.mapper.{Index, _}
import net.liftweb.util.Helpers.tryo
import scala.concurrent.ExecutionContext.Implicits.global
import scala.collection.immutable.List
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object MappedMetrics extends APIMetrics with MdcLoggable{

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,25 +16,20 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.metrics
import java.util.Date
import code.bankconnectors.OBPQueryParam
import code.api.util.OBPQueryParam
import net.liftweb.common.Box
import net.liftweb.mongodb.record.field.{DateField, ObjectIdPk}
import net.liftweb.mongodb.record.{MongoMetaRecord, MongoRecord}

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,28 +16,23 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.model
import java.util.Date
import code.api.util.ErrorMessages._
import code.accountholder.AccountHolders
import code.api.util.APIUtil.hasEntitlement
import code.api.util.{APIUtil, ApiRole, CallContext, ErrorMessages}
import code.bankconnectors.{Connector, OBPQueryParam}
import code.api.util.APIUtil.unboxFullOrFail
import code.api.util.ErrorMessages._
import code.api.util.{APIUtil, CallContext, ErrorMessages, OBPQueryParam}
import code.bankconnectors.Connector
import code.customer.Customer
import code.metadata.comments.Comments
import code.metadata.counterparties.{Counterparties, CounterpartyTrait}
@ -47,14 +42,14 @@ import code.metadata.transactionimages.TransactionImages
import code.metadata.wheretags.WhereTags
import code.util.Helper
import code.util.Helper.MdcLoggable
import code.views.{MapperViews, Views}
import code.views.Views
import net.liftweb.common._
import net.liftweb.json.JObject
import net.liftweb.json.JsonAST.JArray
import net.liftweb.json.JsonDSL._
import net.liftweb.util.Props
import scala.collection.immutable.{List, Set}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.math.BigDecimal
@ -524,6 +519,21 @@ trait BankAccount extends MdcLoggable {
else
viewNotAllowed(view)
}
final def moderatedTransactionFuture(bankId: BankId, accountId: AccountId, transactionId: TransactionId, view: View, user: Box[User], callContext: Option[CallContext] = None) : Future[Box[(ModeratedTransaction, Option[CallContext])]] = {
if(APIUtil.hasAccess(view, user))
for{
(transaction, callContext)<-Connector.connector.vend.getTransactionFuture(bankId, accountId, transactionId, callContext) map {
x => (unboxFullOrFail(x._1, callContext, ConnectorEmptyResponse, 400), x._2)
}
} yield {
view.moderateTransaction(transaction) match {
case Full(m) => Full((m, callContext))
case _ => Failure("Server error - moderateTransactionsWithSameAccount")
}
}
else
Future(viewNotAllowed(view))
}
/*
end views
@ -538,6 +548,21 @@ trait BankAccount extends MdcLoggable {
} yield (moderated, callContext)
}
else viewNotAllowed(view)
}
final def getModeratedTransactionsFuture(user : Box[User], view : View, callContext: Option[CallContext], queryParams: OBPQueryParam* ): Future[Box[(List[ModeratedTransaction],Option[CallContext])]] = {
if(APIUtil.hasAccess(view, user)) {
for {
(transactions, callContext) <- Connector.connector.vend.getTransactionsFuture(bankId, accountId, callContext, queryParams: _*) map {
x => (unboxFullOrFail(x._1, callContext, ConnectorEmptyResponse, 400), x._2)
}
} yield {
view.moderateTransactionsWithSameAccount(transactions) match {
case Full(m) => Full((m, callContext))
case _ => Failure("Server error - moderateTransactionsWithSameAccount")
}
}
}
else Future(viewNotAllowed(view))
}
// TODO We should extract params (and their defaults) prior to this call, so this whole function can be cached.

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.model
import java.util.Date

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.model
import java.util.Date

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.model
import java.util.{Date, UUID}
@ -46,6 +41,7 @@ import net.liftweb.mapper.{LongKeyedMetaMapper, _}
import net.liftweb.util.Helpers.{now, _}
import net.liftweb.util.{FieldError, Helpers, Props}
import code.util.Helper.MdcLoggable
import com.github.dwickern.macros.NameOf
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
@ -71,7 +67,7 @@ object TokenType {
}
object MappedConsumersProvider extends ConsumersProvider {
object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
override def getConsumerByPrimaryIdFuture(id: Long): Future[Box[Consumer]] = {
Future(
@ -339,6 +335,15 @@ object MappedConsumersProvider extends ConsumersProvider {
}
}
}
override def populateMissingUUIDs(): Boolean = {
logger.warn("Executed script: MappedConsumersProvider." + NameOf.nameOf(populateMissingUUIDs))
for {
consumer <- Consumer.findAll(NullRef(Consumer.consumerId))
} yield {
consumer.consumerId(APIUtil.generateUUID()).save()
}
}.forall(_ == true)
}

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.model

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,19 +16,14 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.model

View File

@ -1,6 +1,6 @@
/**
Open Bank Project - API
Copyright (C) 2011-2018, TESOBE Ltd
Copyright (C) 2011-2018, TESOBE Ltd.
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
@ -16,35 +16,30 @@ 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 Ltd
Osloerstrasse 16/17
TESOBE Ltd.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Stefan Bethge : stefan AT tesobe DOT com
Everett Sochowski : everett AT tesobe DOT com
Ayoub Benali: ayoub AT tesobe DOT com
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
*/
package code.model.dataAccess
import java.util.Date
import code.bankconnectors.{OBPLimit, OBPOffset, OBPOrdering, _}
import code.api.util._
import code.model._
import code.util.Helper
import code.util.Helper.MdcLoggable
import com.mongodb.QueryBuilder
import net.liftweb.common._
import net.liftweb.mongodb.BsonDSL._
import net.liftweb.mongodb.{Limit, Skip}
import net.liftweb.mongodb.record.{MongoMetaRecord, MongoRecord}
import net.liftweb.mongodb.record.field.{DateField, ObjectIdPk, ObjectIdRefField}
import net.liftweb.mongodb.record.{MongoMetaRecord, MongoRecord}
import net.liftweb.mongodb.{Limit, Skip}
import net.liftweb.record.field.{DecimalField, StringField}
import code.util.Helper.MdcLoggable
import scala.collection.immutable.List

Some files were not shown because too many files have changed in this diff Show More