mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 19:56:59 +00:00
319 lines
12 KiB
Scala
319 lines
12 KiB
Scala
/**
|
|
Open Bank Project - API
|
|
Copyright (C) 2011-2016, 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.v1_0
|
|
|
|
import net.liftweb.http.rest._
|
|
import net.liftweb.json.JsonDSL._
|
|
import _root_.net.liftweb.common._
|
|
import _root_.net.liftweb.http._
|
|
import _root_.net.liftweb.util.Helpers._
|
|
import code.model._
|
|
import code.api.OAuthHandshake._
|
|
import net.liftweb.util.Helpers.now
|
|
import _root_.net.liftweb.json.Serialization
|
|
import net.liftweb.json.NoTypeHints
|
|
import code.api.OAuthHandshake.getUser
|
|
import code.api.util.APIUtil.getCorrelationId
|
|
import code.bankconnectors._
|
|
import net.liftweb.json.JsonAST.JObject
|
|
import code.bankconnectors.OBPToDate
|
|
import net.liftweb.http.InMemoryResponse
|
|
import net.liftweb.common.Full
|
|
import code.metrics.APIMetrics
|
|
import code.util.Helper.MdcLoggable
|
|
|
|
object OBPAPI1_0 extends RestHelper with MdcLoggable {
|
|
import java.text.SimpleDateFormat
|
|
|
|
implicit val _formats = Serialization.formats(NoTypeHints)
|
|
|
|
val dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
|
|
|
|
private def logAPICall = {
|
|
val correlationId = getCorrelationId()
|
|
APIMetrics.apiMetrics.vend.saveMetric(S.uriAndQueryString.getOrElse(""), (now: TimeSpan), -1L, correlationId)
|
|
}
|
|
|
|
|
|
serve("obp" / "v1.0" prefix {
|
|
|
|
case Nil JsonGet json => {
|
|
//log the API call
|
|
logAPICall
|
|
|
|
// NOTE: This function has been pulled out to gitCommit in APIUtil.scala
|
|
// Not updating this code since its 1.0
|
|
def gitCommit : String = {
|
|
val commit = tryo{
|
|
val properties = new java.util.Properties()
|
|
properties.load(getClass().getClassLoader().getResourceAsStream("git.properties"))
|
|
properties.getProperty("git.commit.id", "")
|
|
}
|
|
commit getOrElse ""
|
|
}
|
|
|
|
val apiDetails = {
|
|
("api" ->
|
|
("version" -> "1.0") ~
|
|
("git_commit" -> gitCommit) ~
|
|
("hosted_by" ->
|
|
("organisation" -> "TESOBE") ~
|
|
("email" -> "contact@tesobe.com") ~
|
|
("phone" -> "+49 (0)30 8145 3994"))) ~
|
|
("links" ->
|
|
("rel" -> "banks") ~
|
|
("href" -> "/banks") ~
|
|
("method" -> "GET") ~
|
|
("title" -> "Returns a list of banks supported on this server"))
|
|
}
|
|
|
|
JsonResponse(apiDetails)
|
|
}
|
|
|
|
case BankId(bankId) :: "accounts" :: AccountId(accountId) :: "transactions" :: ViewId(viewName) :: Nil JsonGet json => {
|
|
|
|
//log the API call
|
|
logAPICall
|
|
|
|
import code.api.OAuthHandshake._
|
|
val (httpCode, data, oAuthParameters) = validator("protectedResource", "GET")
|
|
val headers = ("Content-type" -> "application/x-www-form-urlencoded") :: Nil
|
|
|
|
def asInt(s: Box[String], default: Int): Int = {
|
|
s match {
|
|
case Full(str) => tryo { str.toInt } getOrElse default
|
|
case _ => default
|
|
}
|
|
}
|
|
val limit = asInt(json.header("obp_limit"), 50)
|
|
val offset = asInt(json.header("obp_offset"), 0)
|
|
/**
|
|
* sortBy is currently disabled as it would open up a security hole:
|
|
*
|
|
* sortBy as currently implemented will take in a parameter that searches on the mongo field names. The issue here
|
|
* is that it will sort on the true value, and not the moderated output. So if a view is supposed to return an alias name
|
|
* rather than the true value, but someone uses sortBy on the other bank account name/holder, not only will the returned data
|
|
* have the wrong order, but information about the true account holder name will be exposed due to its position in the sorted order
|
|
*
|
|
* This applies to all fields that can have their data concealed... which in theory will eventually be most/all
|
|
*
|
|
*/
|
|
//val sortBy = json.header("obp_sort_by")
|
|
val sortBy = None
|
|
val sortDirection = OBPOrder(json.header("obp_sort_by"))
|
|
val fromDate = tryo{dateFormat.parse(json.header("obp_from_date") getOrElse "")}.map(OBPFromDate(_))
|
|
val toDate = tryo{dateFormat.parse(json.header("obp_to_date") getOrElse "")}.map(OBPToDate(_))
|
|
|
|
val basicParams = List(OBPLimit(limit),
|
|
OBPOffset(offset),
|
|
OBPOrdering(sortBy, sortDirection))
|
|
val params : List[OBPQueryParam] = fromDate.toList ::: toDate.toList ::: basicParams
|
|
val response = for {
|
|
bankAccount <- BankAccount(bankId, accountId)
|
|
view <- View.fromUrl(viewName, bankAccount)
|
|
transactions <- bankAccount.getModeratedTransactions(getUser(httpCode,oAuthParameters.get("oauth_token")), view, params : _*)
|
|
} yield {
|
|
JsonResponse("transactions" -> transactions.map(t => t.toJson(view)))
|
|
}
|
|
|
|
response match {
|
|
case Full(r) => () => Full(r)
|
|
case _ => () => Full(InMemoryResponse(data.getBytes, headers, Nil, 401))
|
|
}
|
|
|
|
}
|
|
|
|
case BankId(bankId) :: "accounts" :: AccountId(accountId) :: "transactions" ::
|
|
TransactionId(transactionId) :: "transaction" :: ViewId(viewName) :: Nil JsonGet json => {
|
|
|
|
//log the API call
|
|
logAPICall
|
|
|
|
val (httpCode, data, oAuthParameters) = validator("protectedResource", "GET")
|
|
val user = getUser(httpCode,oAuthParameters.get("oauth_token"))
|
|
|
|
val moderatedTransactionAndView = for {
|
|
bank <- Bank(bankId) ?~ { "bank " + bankId + " not found"} ~> 404
|
|
account <- BankAccount(bankId, accountId) ?~ { "account " + accountId + " not found for bank"} ~> 404
|
|
view <- View.fromUrl(viewName, account) ?~ { "view " + viewName + " not found for account"} ~> 404
|
|
moderatedTransaction <- account.moderatedTransaction(transactionId, view, user) ?~ "view/transaction not authorised" ~> 401
|
|
} yield {
|
|
(moderatedTransaction, view)
|
|
}
|
|
|
|
val links : List[JObject] = Nil
|
|
|
|
moderatedTransactionAndView.map(mtAndView => JsonResponse(("transaction" -> mtAndView._1.toJson(mtAndView._2)) ~
|
|
("links" -> links)))
|
|
}
|
|
|
|
case BankId(bankId) :: "accounts" :: AccountId(accountId) :: "transactions" ::
|
|
TransactionId(transactionId) :: "comments" :: ViewId(viewName) :: Nil JsonGet json => {
|
|
|
|
//log the API call
|
|
logAPICall
|
|
|
|
val (httpCode, data, oAuthParameters) = validator("protectedResource", "GET")
|
|
val user = getUser(httpCode,oAuthParameters.get("oauth_token"))
|
|
|
|
val comments = for {
|
|
bank <- Bank(bankId) ?~ { "bank " + bankId + " not found"} ~> 404
|
|
account <- BankAccount(bankId, accountId) ?~ { "account " + accountId + " not found for bank"} ~> 404
|
|
view <- View.fromUrl(viewName,account) ?~ { "view " + viewName + " not found for account"} ~> 404
|
|
moderatedTransaction <- account.moderatedTransaction(transactionId, view, user) ?~ "view/transaction not authorised" ~> 401
|
|
comments <- Box(moderatedTransaction.metadata).flatMap(_.comments) ?~ "transaction metadata not authorised" ~> 401
|
|
} yield comments
|
|
|
|
val links : List[JObject] = Nil
|
|
|
|
comments.map(cs => JsonResponse(("comments" -> cs.map(_.toJson)) ~
|
|
("links" -> links)))
|
|
}
|
|
|
|
case BankId(bankId) :: "accounts" :: Nil JsonGet json => {
|
|
|
|
//log the API call
|
|
logAPICall
|
|
|
|
val (httpCode, data, oAuthParameters) = validator("protectedResource", "GET")
|
|
val headers = ("Content-type" -> "application/x-www-form-urlencoded") :: Nil
|
|
val user = getUser(httpCode,oAuthParameters.get("oauth_token"))
|
|
|
|
def bankAccountSet2JsonResponse(bankAccounts: Set[BankAccount]): LiftResponse = {
|
|
val accJson = bankAccounts.map(bAcc => bAcc.overviewJson(user))
|
|
JsonResponse(("accounts" -> accJson))
|
|
}
|
|
|
|
Bank(bankId) match {
|
|
case Full(bank) =>
|
|
{
|
|
if(httpCode == 200)
|
|
{
|
|
bank.accountv12AndBelow(user) match {
|
|
case Full(a) => bankAccountSet2JsonResponse(a.toSet)
|
|
case _ => InMemoryResponse("no account found".getBytes, Nil, Nil, 404)
|
|
}
|
|
}
|
|
else
|
|
InMemoryResponse(data.getBytes, Nil, Nil, httpCode)
|
|
}
|
|
case _ => {
|
|
val error = "bank " + bankId + " not found"
|
|
InMemoryResponse(error.getBytes(), headers, Nil, 404)
|
|
}
|
|
}
|
|
}
|
|
|
|
case BankId(bankId) :: "accounts" :: AccountId(accountId) :: "account" :: ViewId(viewName) :: Nil JsonGet json => {
|
|
|
|
//log the API call
|
|
logAPICall
|
|
|
|
val (httpCode, data, oAuthParameters) = validator("protectedResource", "GET")
|
|
val headers = ("Content-type" -> "application/x-www-form-urlencoded") :: Nil
|
|
val user = getUser(httpCode,oAuthParameters.get("oauth_token"))
|
|
|
|
case class ModeratedAccountAndViews(account: ModeratedBankAccount, views: List[View])
|
|
|
|
val moderatedAccountAndViews = for {
|
|
bank <- Bank(bankId) ?~ { "bank " + bankId + " not found"} ~> 404
|
|
account <- BankAccount(bankId, accountId) ?~ { "account " + accountId + " not found for bank"} ~> 404
|
|
view <- View.fromUrl(viewName, account) ?~ { "view " + viewName + " not found for account"} ~> 404
|
|
moderatedAccount <- account.moderatedBankAccount(view, user) ?~ {"view/account not authorised"} ~> 401
|
|
availableViews <- Full(account.permittedViews(user))
|
|
} yield ModeratedAccountAndViews(moderatedAccount, availableViews)
|
|
|
|
def linkJson(view: View): JObject = {
|
|
("rel" -> view.name) ~
|
|
("href" -> { "/" + bankId + "/accounts/" + accountId + "/transactions/" + view.viewId.value }) ~
|
|
("method" -> "GET") ~
|
|
("title" -> view.description)
|
|
}
|
|
|
|
def bankAccountMetaData(mv : ModeratedAccountAndViews) = {
|
|
("views_available" -> mv.views.map(_.toJson)) ~
|
|
("links" -> mv.views.map(linkJson))
|
|
}
|
|
|
|
moderatedAccountAndViews.map(mv => JsonResponse("account" -> mv.account.toJson ~ bankAccountMetaData(mv)))
|
|
}
|
|
|
|
case BankId(bankId) :: "offices" :: Nil JsonGet json => {
|
|
|
|
//log the API call
|
|
logAPICall
|
|
|
|
//TODO: An office model needs to be created
|
|
val offices : List[JObject] = Nil
|
|
JsonResponse("offices" -> offices)
|
|
}
|
|
|
|
case BankId(bankId) :: "bank" :: Nil JsonGet json => {
|
|
|
|
//log the API call
|
|
logAPICall
|
|
|
|
def links = {
|
|
def accounts = {
|
|
("rel" -> "accounts") ~
|
|
("href" -> {"/" + bankId + "/accounts"}) ~
|
|
("method" -> "GET") ~
|
|
("title" -> "Get list of accounts available")
|
|
}
|
|
|
|
def offices = {
|
|
("rel" -> "offices") ~
|
|
("href" -> {"/" + bankId + "/offices"}) ~
|
|
("method" -> "GET") ~
|
|
("title" -> "Get list of offices")
|
|
}
|
|
|
|
List(accounts, offices)
|
|
}
|
|
|
|
val bank = for {
|
|
bank <- Bank(bankId) ?~ { "bank " + bankId + " not found"} ~> 404
|
|
} yield bank
|
|
|
|
bank.map(b => JsonResponse(b.detailedJson ~ ("links" -> links)))
|
|
}
|
|
|
|
case "banks" :: Nil JsonGet json => {
|
|
|
|
//log the API call
|
|
logAPICall
|
|
|
|
JsonResponse("banks" -> Bank.toJson(Bank.all.getOrElse(Nil)))
|
|
}
|
|
})
|
|
|
|
} |