From cf1e83b5a3cd4df81cbb44120046975f0335749b Mon Sep 17 00:00:00 2001 From: Ayoub BENALI Date: Fri, 14 Jun 2013 10:31:36 +0200 Subject: [PATCH] updated the API project files --- .gitignore | 7 +- MavLift/.gitignore | 3 +- MavLift/pom.xml | 614 +- MavLift/project/build.properties | 2 +- MavLift/project/build.scala | 92 +- MavLift/project/plugins.sbt | 3 +- .../src/main/java/code/pgp/PgpEncryption.java | 321 + .../main/scala/bootstrap/liftweb/Boot.scala | 205 +- .../src/main/scala/code/api/OBPAPI1.0.scala | 405 ++ .../src/main/scala/code/api/OBPAPI1.1.scala | 1873 ++++++ .../main/scala/code/api/OBPRestHelper.scala | 87 + .../src/main/scala/code/api/bankmock.scala | 172 + MavLift/src/main/scala/code/api/cash.scala | 186 + .../src/main/scala/code/api/importer.scala | 263 + .../src/main/scala/code/api/oauth1.0.scala | 502 ++ .../scala/code/api/v1_2/JSONFactory.scala | 546 ++ .../main/scala/code/api/v1_2/OBPAPI1.2.scala | 1175 ++++ .../main/scala/code/model/BankingData.scala | 365 ++ .../src/main/scala/code/model/Metadata.scala | 199 + .../code/model/ModeratedBankingData.scala | 306 + MavLift/src/main/scala/code/model/OAuth.scala | 109 +- .../scala/code/model/{traits => }/User.scala | 34 +- MavLift/src/main/scala/code/model/View.scala | 962 +++ .../scala/code/model/dataAccess/Account.scala | 141 +- .../scala/code/model/dataAccess/Admin.scala | 108 + .../code/model/dataAccess/Connectors.scala | 709 ++- .../code/model/dataAccess/MongoConfig.scala | 18 +- .../model/dataAccess/OBPTransaction.scala | 479 +- .../scala/code/model/dataAccess/OBPUser.scala | 249 +- .../metrics.scala} | 23 +- .../implementedTraits/BankAccountImpl.scala | 124 - .../model/implementedTraits/BankImpl.scala | 43 - .../implementedTraits/MetadataImpl.scala | 59 - .../OtherBankAccountImpl.scala | 49 - .../implementedTraits/TransactionImpl.scala | 59 - .../model/implementedTraits/ViewsImpl.scala | 223 - .../code/model/traits/AccountOwner.scala | 43 - .../main/scala/code/model/traits/Bank.scala | 76 - .../scala/code/model/traits/BankAccount.scala | 131 - .../scala/code/model/traits/Comment.scala | 67 - .../scala/code/model/traits/Metadata.scala | 52 - .../scala/code/model/traits/Moderated.scala | 233 - .../scala/code/model/traits/Transaction.scala | 69 - .../main/scala/code/model/traits/View.scala | 421 -- .../code/snippet/AccountRegistration.scala | 189 + .../code/snippet/ConsumerRegistration.scala | 126 + .../scala/code/snippet/CustomEditable.scala | 105 - MavLift/src/main/scala/code/snippet/Nav.scala | 221 + .../code/snippet/OAuthAuthorisation.scala | 149 + .../scala/code/snippet/OAuthHandshake.scala | 552 -- .../src/main/scala/code/snippet/OBPREST.scala | 488 -- .../src/main/scala/code/util/APIUtil.scala | 62 + MavLift/src/main/webapp/connect.html | 166 + .../main/webapp/consumer-registration.html | 105 + MavLift/src/main/webapp/index.html | 12 +- .../metrics.html} | 27 +- .../main/webapp/templates-hidden/default.html | 65 +- MavLift/src/test/scala/LiftConsole.scala | 3 +- MavLift/src/test/scala/RunWebApp.scala | 5 +- .../src/test/scala/code/api/API11Test.scala | 30 + .../src/test/scala/code/api/API12Test.scala | 5493 +++++++++++++++++ .../src/test/scala/code/api/ServerSetup.scala | 195 + README | 53 - 63 files changed, 16030 insertions(+), 3823 deletions(-) create mode 100644 MavLift/src/main/java/code/pgp/PgpEncryption.java create mode 100644 MavLift/src/main/scala/code/api/OBPAPI1.0.scala create mode 100644 MavLift/src/main/scala/code/api/OBPAPI1.1.scala create mode 100644 MavLift/src/main/scala/code/api/OBPRestHelper.scala create mode 100644 MavLift/src/main/scala/code/api/bankmock.scala create mode 100644 MavLift/src/main/scala/code/api/cash.scala create mode 100644 MavLift/src/main/scala/code/api/importer.scala create mode 100644 MavLift/src/main/scala/code/api/oauth1.0.scala create mode 100644 MavLift/src/main/scala/code/api/v1_2/JSONFactory.scala create mode 100644 MavLift/src/main/scala/code/api/v1_2/OBPAPI1.2.scala create mode 100644 MavLift/src/main/scala/code/model/BankingData.scala create mode 100644 MavLift/src/main/scala/code/model/Metadata.scala create mode 100644 MavLift/src/main/scala/code/model/ModeratedBankingData.scala rename MavLift/src/main/scala/code/model/{traits => }/User.scala (66%) create mode 100644 MavLift/src/main/scala/code/model/View.scala create mode 100644 MavLift/src/main/scala/code/model/dataAccess/Admin.scala rename MavLift/src/main/scala/code/model/{traits/OtherBankAccount.scala => dataAccess/metrics.scala} (71%) delete mode 100644 MavLift/src/main/scala/code/model/implementedTraits/BankAccountImpl.scala delete mode 100644 MavLift/src/main/scala/code/model/implementedTraits/BankImpl.scala delete mode 100644 MavLift/src/main/scala/code/model/implementedTraits/MetadataImpl.scala delete mode 100644 MavLift/src/main/scala/code/model/implementedTraits/OtherBankAccountImpl.scala delete mode 100644 MavLift/src/main/scala/code/model/implementedTraits/TransactionImpl.scala delete mode 100644 MavLift/src/main/scala/code/model/implementedTraits/ViewsImpl.scala delete mode 100644 MavLift/src/main/scala/code/model/traits/AccountOwner.scala delete mode 100644 MavLift/src/main/scala/code/model/traits/Bank.scala delete mode 100644 MavLift/src/main/scala/code/model/traits/BankAccount.scala delete mode 100644 MavLift/src/main/scala/code/model/traits/Comment.scala delete mode 100644 MavLift/src/main/scala/code/model/traits/Metadata.scala delete mode 100644 MavLift/src/main/scala/code/model/traits/Moderated.scala delete mode 100644 MavLift/src/main/scala/code/model/traits/Transaction.scala delete mode 100644 MavLift/src/main/scala/code/model/traits/View.scala create mode 100644 MavLift/src/main/scala/code/snippet/AccountRegistration.scala create mode 100644 MavLift/src/main/scala/code/snippet/ConsumerRegistration.scala delete mode 100644 MavLift/src/main/scala/code/snippet/CustomEditable.scala create mode 100644 MavLift/src/main/scala/code/snippet/Nav.scala create mode 100644 MavLift/src/main/scala/code/snippet/OAuthAuthorisation.scala delete mode 100644 MavLift/src/main/scala/code/snippet/OAuthHandshake.scala delete mode 100644 MavLift/src/main/scala/code/snippet/OBPREST.scala create mode 100644 MavLift/src/main/scala/code/util/APIUtil.scala create mode 100644 MavLift/src/main/webapp/connect.html create mode 100644 MavLift/src/main/webapp/consumer-registration.html rename MavLift/src/main/{scala/code/model/implementedTraits/AccountOwnerImpl.scala => webapp/metrics.html} (73%) create mode 100644 MavLift/src/test/scala/code/api/API11Test.scala create mode 100644 MavLift/src/test/scala/code/api/API12Test.scala create mode 100644 MavLift/src/test/scala/code/api/ServerSetup.scala delete mode 100644 README diff --git a/.gitignore b/.gitignore index 3a2f9de54..e82aac2e8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .settings .classpath .project -MavLift/.cache -MavLift/src/main/resources/rebel.xml -MavLift/src/main/resources/props \ No newline at end of file +.cache +target +MavLift/src/main/resources/ +MavLift/src/test/resources/ \ No newline at end of file diff --git a/MavLift/.gitignore b/MavLift/.gitignore index 2864d53dc..8b1378917 100644 --- a/MavLift/.gitignore +++ b/MavLift/.gitignore @@ -1,2 +1 @@ -target -src/main/resources/ + diff --git a/MavLift/pom.xml b/MavLift/pom.xml index b099becc6..dfa6774a0 100644 --- a/MavLift/pom.xml +++ b/MavLift/pom.xml @@ -1,295 +1,347 @@ - + - - 4.0.0 - com.tesobe - opan_bank - 1.0 - war - Opan Bank - 2011 - - 2.9.1 - - UTF-8 - ${project.build.sourceEncoding} - - 1.2-m1 - scaladocs/ - http://scala-tools.org/mvnsites/liftweb - - - - sonatype.releases - sonatype Dependencies Repository for Releases - https://oss.sonatype.org/content/groups/scala-tools - - - sonatype.snapshots - sonatype Dependencies Repository for Snapshots - https://oss.sonatype.org/content/repositories/snapshots - - - scala-tools.releases - Scala-Tools Dependencies Repository for Releases - http://scala-tools.org/repo-releases - - - scala-tools.snapshots - Scala-Tools Dependencies Repository for Snapshots - http://scala-tools.org/repo-snapshots - - + 4.0.0 + com.tesobe + OBP-API + 1.0 + war + Open Bank Project API + 2011 + + 2.9.1 + 2.4 + + UTF-8 + ${project.build.sourceEncoding} + + 1.2-m1 + scaladocs/ + http://scala-tools.org/mvnsites/liftweb + - + + + scala-tools.releases + Scala-Tools Dependencies Repository for Releases + https://oss.sonatype.org/content/groups/scala-tools/ + + + java.net.maven2 + java.net Maven2 Repository + http://download.java.net/maven/2/ + + + scala-tools.snapshots + Scala-Tools Dependencies Repository for Snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + + - sonatype.releases - sonatype Dependencies Repository for Releases - https://oss.sonatype.org/content/groups/scala-tools + org.sonatype.oss.groups.public + Sonatype Public + https://oss.sonatype.org/content/groups/public - - scala-tools.releases - Scala-Tools Plugins Repository for Releases - http://scala-tools.org/repo-releases - - + - + + + net.liftweb + lift-mapper_${scala.version} + ${lift.version} + + + net.liftweb + lift-widgets_${scala.version} + ${lift.version} + + + net.databinder + dispatch-http_${scala.version} + 0.8.6 + + + net.databinder + dispatch-oauth_${scala.version} + 0.8.6 + - net.liftweb - lift-mapper_2.9.1 - 2.4-M4 + net.databinder + dispatch-lift-json_${scala.version} + 0.8.5 - - net.liftweb - lift-widgets_2.9.1 - 2.4-M4 - - - net.databinder - dispatch-http_${scala.version} - 0.8.6 - - - net.databinder - dispatch-oauth_${scala.version} - 0.8.6 - - - ch.qos.logback - logback-classic - 0.9.26 - - - postgresql - postgresql - 8.4-701.jdbc4 - - - com.h2database - h2 - 1.2.138 - runtime - - - javax.servlet - servlet-api - 2.5 - provided - - - junit - junit - 4.7 - test - - - org.scala-tools.testing - specs_2.9.1 - 1.6.9 - test - - - org.mortbay.jetty - jetty - 6.1.25 - test - - - org.bouncycastle - bcpg-jdk16 - 1.46 - - + + ch.qos.logback + logback-classic + 0.9.26 + + + postgresql + postgresql + 8.4-701.jdbc4 + + + com.h2database + h2 + 1.2.138 + runtime + + + javax.servlet + servlet-api + 2.5 + provided + + + junit + junit + 4.7 + test + + + org.scalatest + scalatest_${scala.version} + 2.0.M5 + test + + + org.scala-tools.testing + specs_${scala.version} + 1.6.9 + test + + + org.mortbay.jetty + jetty + 6.1.25 + test + + + org.bouncycastle + bcpg-jdk16 + 1.46 + + + + org.scala-lang + scala-compiler + ${scala.version} + test + org.scala-lang - scala-compiler - ${scala.version} - test + scala-library + 2.9.1 - - net.liftweb - lift-mongodb_2.9.1 - 2.4 - - - net.liftweb - lift-mongodb-record_2.9.1 - 2.4 - - - org.mongodb - casbah_${scala.version} - 2.3.0 - compile - pom - - + + net.liftweb + lift-mongodb_${scala.version} + ${lift.version} + + + net.liftweb + lift-mongodb-record_${scala.version} + ${lift.version} + + + org.mongodb + casbah_${scala.version} + 2.3.0 + compile + pom + + - - src/main - src/test/scala - - - org.scala-tools - maven-scala-plugin - 2.9 - - ${project.build.sourceEncoding} - - -Xmx1024m - -DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties - - - - - - compile - testCompile - - - - - - org.apache.maven.plugins - maven-war-plugin - 2.1.1 - - - org.apache.maven.plugins - maven-resources-plugin - 2.4.2 - - - default-copy-resources - process-resources - - copy-resources - - - true - ${project.build.directory} - - - ${project.basedir}/src - - packageLinkDefs.properties - - true - - - - - - - - org.mortbay.jetty - maven-jetty-plugin - 6.1.25 - - / - 5 - - - - net.sf.alchim - yuicompressor-maven-plugin - 0.7.1 - - - - compress - - - - - true - - - - org.apache.maven.plugins - maven-idea-plugin - 2.2 - - true - - - - org.apache.maven.plugins - maven-eclipse-plugin - 2.7 - - true - - ch.epfl.lamp.sdt.core.scalanature - - - ch.epfl.lamp.sdt.core.scalabuilder - - - ch.epfl.lamp.sdt.launching.SCALA_CONTAINER - org.eclipse.jdt.launching.JRE_CONTAINER - - - - - pl.project13.maven - git-commit-id-plugin - 2.1.0 - - - - revision - - - - - ${project.basedir}/.git - true - src/main/resources/git.properties - false - - - - - - - - org.scala-tools - maven-scala-plugin - 2.14.3 - - ${project.build.sourceEncoding} - - -Xmx1024m - -DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties - - - - - + + src/main/scala + src/test/scala + + + org.apache.maven.plugins + maven-surefire-plugin + 2.7 + + true + + + + + org.scalatest + maven-scalatest-plugin + 1.0-SNAPSHOT + + ${project.build.directory}/surefire-reports + . + WDF TestSuite.txt + -Drun.mode=test + + + + test + + test + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + generate-sources + + add-source + + + + src/main/java + + + + + + + org.scala-tools + maven-scala-plugin + 2.15.2 + + ${project.build.sourceEncoding} + + -Xmx1024m + -DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties + + + + + + compile + testCompile + + + + + + org.apache.maven.plugins + maven-war-plugin + 2.3 + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + default-copy-resources + process-resources + + copy-resources + + + true + ${project.build.directory} + + + ${project.basedir}/src + + packageLinkDefs.properties + + true + + + + + + + + org.mortbay.jetty + maven-jetty-plugin + 6.1.25 + + / + 5 + + + + net.sf.alchim + yuicompressor-maven-plugin + 0.7.1 + + + + compress + + + + + true + + + + org.apache.maven.plugins + maven-idea-plugin + 2.2 + + true + + + + org.apache.maven.plugins + maven-eclipse-plugin + 2.7 + + true + + ch.epfl.lamp.sdt.core.scalanature + + + ch.epfl.lamp.sdt.core.scalabuilder + + + ch.epfl.lamp.sdt.launching.SCALA_CONTAINER + org.eclipse.jdt.launching.JRE_CONTAINER + + + + + pl.project13.maven + git-commit-id-plugin + 2.1.0 + + + + revision + + + + + ${project.basedir}/.git + true + src/main/resources/git.properties + false + + + + + + + + org.scala-tools + maven-scala-plugin + 2.15.2 + + ${project.build.sourceEncoding} + + -Xmx1024m + -DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties + + + + + diff --git a/MavLift/project/build.properties b/MavLift/project/build.properties index 1fd04292d..b6c3c3270 100755 --- a/MavLift/project/build.properties +++ b/MavLift/project/build.properties @@ -1,2 +1,2 @@ #Project properties -sbt.version=0.11.2 +sbt.version=0.11.3 diff --git a/MavLift/project/build.scala b/MavLift/project/build.scala index a1060a96e..9dc1a79d4 100755 --- a/MavLift/project/build.scala +++ b/MavLift/project/build.scala @@ -1,3 +1,5 @@ + + import sbt._ import Keys._ import com.github.siasia._ @@ -9,9 +11,9 @@ object LiftProjectBuild extends Build { override lazy val settings = super.settings ++ buildSettings lazy val buildSettings = Seq( - organization := "com.tesobe", - version := "0.1", - scalaVersion := "2.9.1") + organization := pom.groupId, + version := pom.version + ) def yourWebSettings = webSettings ++ Seq( // If you are use jrebel @@ -19,40 +21,74 @@ object LiftProjectBuild extends Build { ) lazy val opanBank = Project( - "OpanBank", + pom.artifactId, base = file("."), - settings = defaultSettings ++ yourWebSettings) + settings = defaultSettings ++ yourWebSettings ++ pom.settings) + + object pom { + + val pomFile = "pom.xml" + lazy val pom = xml.XML.loadFile(pomFile) + + lazy val pomProperties = (for{ + props <- (pom \ "properties") + p <- props.child + } yield { + p.label -> p.text + }).toMap + + private lazy val PropertiesExpr = """.*\$\{(.*?)\}.*""".r + + def populateProps(t: String) = t match { + case PropertiesExpr(p) => { + val replaceWith = pomProperties.get(p) + t.replace("${"+p+"}", replaceWith.getOrElse(throw new Exception("Cannot find property: '" + p + "' required by: '" + t + "' in: " + pomFile))) + } + case _ => t + } + + lazy val pomDeps = (for{ + dep <- pom \ "dependencies" \ "dependency" + } yield { + val scope = (dep \ "scope") + val groupId = (dep \ "groupId").text + val noScope = populateProps(groupId) % populateProps((dep \ "artifactId").text) % populateProps((dep \ "version").text) + val nonCustom = if (scope.nonEmpty) noScope % populateProps(scope.text) + else noScope + + if (groupId.endsWith("jetty")) Seq(noScope % "container", nonCustom) //hack to add jetty deps in container scope as it is required by the web plugin + else Seq(nonCustom) + }).flatten + + lazy val pomRepos = for { + rep <- pom \ "repositories" \ "repository" + } yield { + populateProps((rep \ "url").text) at populateProps((rep \ "url").text) + } + + lazy val pomScalaVersion = (pom \ "properties" \ "scala.version").text + + lazy val artifactId = (pom \ "artifactId").text + lazy val groupId = (pom \ "groupId").text + lazy val version = (pom \ "version").text + lazy val name = (pom \ "name").text + + lazy val settings = Seq( + scalaVersion := pomScalaVersion, + libraryDependencies ++= pomDeps, + resolvers ++= pomRepos + ) + + } lazy val defaultSettings = Defaults.defaultSettings ++ Seq( - name := "Opan Bank", + name := pom.name, resolvers ++= Seq( "Typesafe Repo" at "http://repo.typesafe.com/typesafe/releases", "Java.net Maven2 Repository" at "http://download.java.net/maven/2/", "Scala-Tools Dependencies Repository for Releases" at "http://scala-tools.org/repo-releases", "Scala-Tools Dependencies Repository for Snapshots" at "http://scala-tools.org/repo-snapshots"), - libraryDependencies ++= { - val liftVersion = "2.4-M4" - Seq( - //%% means: add current scala version to artifact name - - "net.liftweb" %% "lift-webkit" % liftVersion % "compile", - "net.liftweb" %% "lift-mapper" % liftVersion % "compile", - "net.liftweb" %% "lift-mongodb" % liftVersion % "compile", - "net.liftweb" %% "lift-mongodb-record" % liftVersion % "compile", - "net.liftweb" %% "lift-widgets" % liftVersion % "compile", -// "org.eclipse.jetty" % "jetty-webapp" % "7.5.4.v20111024" % "container", -// "org.eclipse.jetty" % "jetty-webapp" % "8.0.4.v20111024" % "container", - "org.mortbay.jetty" % "jetty" % "6.1.22" % "container", - "javax.servlet" % "servlet-api" % "2.5" % "provided->default", - "ch.qos.logback" % "logback-classic" % "1.0.0" % "compile", - "com.h2database" % "h2" % "1.2.138" % "runtime", - "com.mongodb.casbah" %% "casbah" % "2.1.5-1" % "compile", - "org.scalatest" %% "scalatest" % "1.6.1" % "test", - "org.scala-tools.testing" %% "specs" % "1.6.9" % "test", - "junit" % "junit" % "4.10" % "test") - }, - // compile options scalacOptions ++= Seq("-encoding", "UTF-8", "-deprecation", "-unchecked"), javacOptions ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"), diff --git a/MavLift/project/plugins.sbt b/MavLift/project/plugins.sbt index def69fea0..259302b12 100755 --- a/MavLift/project/plugins.sbt +++ b/MavLift/project/plugins.sbt @@ -13,6 +13,7 @@ libraryDependencies <+= sbtVersion(v => v match { case "0.11.0" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.0-0.2.8" case "0.11.1" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.1-0.2.10" case "0.11.2" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.2-0.2.11" +case "0.11.3" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.3-0.2.11.1" }) //sbteclipse @@ -22,7 +23,7 @@ resolvers += { Resolver.url("Typesafe Repository", typesafeRepoUrl)(pattern) } -addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0") +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0") //sbt-idea resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/" diff --git a/MavLift/src/main/java/code/pgp/PgpEncryption.java b/MavLift/src/main/java/code/pgp/PgpEncryption.java new file mode 100644 index 000000000..8cbf5a462 --- /dev/null +++ b/MavLift/src/main/java/code/pgp/PgpEncryption.java @@ -0,0 +1,321 @@ +package code.pgp; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openpgp.PGPCompressedData; +import org.bouncycastle.openpgp.PGPCompressedDataGenerator; +import org.bouncycastle.openpgp.PGPEncryptedData; +import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; +import org.bouncycastle.openpgp.PGPEncryptedDataList; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPLiteralData; +import org.bouncycastle.openpgp.PGPLiteralDataGenerator; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +import org.bouncycastle.openpgp.PGPUtil; + +/** + * Simple routine to encrypt and decrypt using a Public and Private key with passphrase. This service + * routine provides the basic PGP services between byte arrays. + * + */ +public class PgpEncryption { + + public PgpEncryption() { + // Empty constructor + } + + private static PGPPrivateKey findSecretKey( + PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) + throws PGPException, NoSuchProviderException { + PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID); + + if (pgpSecKey == null) { + return null; + } + + return pgpSecKey.extractPrivateKey(pass, "BC"); + } + + /** + * decrypt the passed in message stream + * + * @param encrypted + * The message to be decrypted. + * @param passPhrase + * Pass phrase (key) + * + * @return Clear text as a byte array. I18N considerations are not handled + * by this routine + * @exception IOException + * @exception PGPException + * @exception NoSuchProviderException + */ + public static byte[] decrypt(byte[] encrypted, InputStream keyIn, char[] password) + throws IOException, PGPException, NoSuchProviderException { + InputStream in = new ByteArrayInputStream(encrypted); + + in = PGPUtil.getDecoderStream(in); + + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc = null; + Object o = pgpF.nextObject(); + + // + // the first object might be a PGP marker packet. + // + if (o instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) o; + } else { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + + + + // + // find the secret key + // + Iterator it = enc.getEncryptedDataObjects(); + PGPPrivateKey sKey = null; + PGPPublicKeyEncryptedData pbe = null; + PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( + PGPUtil.getDecoderStream(keyIn)); + + while (sKey == null && it.hasNext()) { + pbe = (PGPPublicKeyEncryptedData) it.next(); + + sKey = findSecretKey(pgpSec, pbe.getKeyID(), password); + } + + if (sKey == null) { + throw new IllegalArgumentException( + "secret key for message not found."); + } + + InputStream clear = pbe.getDataStream(sKey, "BC"); + + + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + + PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(cData.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject(); + + InputStream unc = ld.getInputStream(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int ch; + + while ((ch = unc.read()) >= 0) { + out.write(ch); + + } + + byte[] returnBytes = out.toByteArray(); + out.close(); + return returnBytes; + } + + /** + * Simple PGP encryptor between byte[]. + * + * @param clearData + * The test to be encrypted + * @param passPhrase + * The pass phrase (key). This method assumes that the key is a + * simple pass phrase, and does not yet support RSA or more + * sophisiticated keying. + * @param fileName + * File name. This is used in the Literal Data Packet (tag 11) + * which is really inly important if the data is to be related to + * a file to be recovered later. Because this routine does not + * know the source of the information, the caller can set + * something here for file name use that will be carried. If this + * routine is being used to encrypt SOAP MIME bodies, for + * example, use the file name from the MIME type, if applicable. + * Or anything else appropriate. + * + * @param armor + * + * @return encrypted data. + * @exception IOException + * @exception PGPException + * @exception NoSuchProviderException + */ + public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey, + String fileName,boolean withIntegrityCheck, boolean armor) + throws IOException, PGPException, NoSuchProviderException { + if (fileName == null) { + fileName = PGPLiteralData.CONSOLE; + } + + ByteArrayOutputStream encOut = new ByteArrayOutputStream(); + + OutputStream out = encOut; + if (armor) { + out = new ArmoredOutputStream(out); + } + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator( + PGPCompressedDataGenerator.ZIP); + OutputStream cos = comData.open(bOut); // open it with the final + // destination + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + + // we want to generate compressed data. This might be a user option + // later, + // in which case we would pass in bOut. + OutputStream pOut = lData.open(cos, // the compressed output stream + PGPLiteralData.BINARY, fileName, // "filename" to store + clearData.length, // length of clear data + new Date() // current time + ); + pOut.write(clearData); + + lData.close(); + comData.close(); + + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator( + PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(), + "BC"); + + cPk.addMethod(encKey); + + byte[] bytes = bOut.toByteArray(); + + OutputStream cOut = cPk.open(out, bytes.length); + + cOut.write(bytes); // obtain the actual bytes from the compressed stream + + cOut.close(); + + out.close(); + + return encOut.toByteArray(); + } + + private static PGPPublicKey readPublicKey(InputStream in) + throws IOException, PGPException { + in = PGPUtil.getDecoderStream(in); + + PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in); + + // + // we just loop through the collection till we find a key suitable for + // encryption, in the real + // world you would probably want to be a bit smarter about this. + // + + // + // iterate through the key rings. + // + Iterator rIt = pgpPub.getKeyRings(); + + while (rIt.hasNext()) { + PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next(); + Iterator kIt = kRing.getPublicKeys(); + + while (kIt.hasNext()) { + PGPPublicKey k = (PGPPublicKey) kIt.next(); + + if (k.isEncryptionKey()) { + return k; + } + } + } + + throw new IllegalArgumentException( + "Can't find encryption key in key ring."); + } + + public static byte[] getBytesFromFile(File file) throws IOException { + InputStream is = new FileInputStream(file); + + // Get the size of the file + long length = file.length(); + + if (length > Integer.MAX_VALUE) { + // File is too large + } + + // Create the byte array to hold the data + byte[] bytes = new byte[(int)length]; + + // Read in the bytes + int offset = 0; + int numRead = 0; + while (offset < bytes.length + && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) { + offset += numRead; + } + + // Ensure all the bytes have been read in + if (offset < bytes.length) { + throw new IOException("Could not completely read file "+file.getName()); + } + + // Close the input stream and return bytes + is.close(); + return bytes; + } + + public static String encryptToFile(String inputStr, String keyFile, String outFile) throws Exception { + Security.addProvider(new BouncyCastleProvider()); + + byte[] original = inputStr.getBytes(); + + FileInputStream pubKey = new FileInputStream(keyFile); + byte[] encrypted = encrypt(original, readPublicKey(pubKey), null, + true, true); + + FileOutputStream dfis = new FileOutputStream(outFile); + dfis.write(encrypted); + dfis.close(); + + return new String(encrypted); + } + + public static String decryptFromFile(String passphrase, String keyFile, String inputFile) throws Exception { + Security.addProvider(new BouncyCastleProvider()); + + byte[] encFromFile = getBytesFromFile(new File(inputFile)); + FileInputStream secKey = new FileInputStream(keyFile); + + byte[] decrypted = decrypt(encFromFile, secKey, passphrase.toCharArray()); + + return new String(decrypted); + } + + public static void main(String[] args) throws Exception { + String encrypted = encryptToFile("Hello world","pub.asc","enc.asc"); + System.out.println("\nencrypted data = '" + new String(encrypted) + "'"); + + String decrypted = decryptFromFile("open sesame", "secret.asc", "enc.asc"); + System.out.println("\ndecrypted data = '" + decrypted + "'"); + + } +} \ No newline at end of file diff --git a/MavLift/src/main/scala/bootstrap/liftweb/Boot.scala b/MavLift/src/main/scala/bootstrap/liftweb/Boot.scala index 0484633b7..d7362d723 100755 --- a/MavLift/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/MavLift/src/main/scala/bootstrap/liftweb/Boot.scala @@ -1,4 +1,4 @@ -/** +/** Open Bank Project - Transparency / Social Finance Web Application Copyright (C) 2011, 2012, TESOBE / Music Pictures Ltd @@ -15,14 +15,14 @@ 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 . -Email: contact@tesobe.com -TESOBE / Music Pictures Ltd +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd Osloerstrasse 16/17 Berlin 13359, Germany This product includes software developed at TESOBE (http://www.tesobe.com/) - by + by Simon Redfern : simon AT tesobe DOT com Stefan Bethge : stefan AT tesobe DOT com Everett Sochowski : everett AT tesobe DOT com @@ -31,7 +31,7 @@ Berlin 13359, Germany */ package bootstrap.liftweb -import code.snippet._ + import net.liftweb._ import util._ import common._ @@ -39,20 +39,18 @@ import http._ import sitemap._ import Loc._ import mapper._ -import code.model.dataAccess.{MongoConfig,OBPUser,Privilege,Account, MongoDBLocalStorage, HostedAccount} -import code.model.{Nonce, Consumer, Token} -import code.model.traits.{Bank, View, ModeratedTransaction} -import code.model.implementedTraits.{BankImpl, Anonymous, View} -import com.tesobe.utils._ +import code.model.dataAccess._ +import code.model.{Nonce, Consumer, Token, Bank, ModeratedTransaction, View, BankAccount} +import code.api._ import net.liftweb.util.Helpers._ import net.liftweb.widgets.tablesorter.TableSorter import net.liftweb.json.JsonDSL._ -import code.snippet.OAuthHandshake +import code.api.OAuthHandshake import net.liftweb.util.Schedule import net.liftweb.mongodb.BsonDSL._ -import code.model.dataAccess.LocalStorage -import code.model.traits.BankAccount import net.liftweb.http.js.jquery.JqJsCmds +import code.snippet.OAuthAuthorisation +import net.liftweb.util.Helpers import javax.mail.{ Authenticator, PasswordAuthentication } /** * A class that's instantiated early and run. It allows the application @@ -65,49 +63,74 @@ class Boot extends Loggable{ MongoConfig.init if (!DB.jndiJdbcConnAvailable_?) { - val driver = Props.get("db.driver") openOr "org.h2.Driver" - val vendor = - new StandardDBVendor(driver, - Props.get("db.url") openOr - "jdbc:h2:lift_proto.db;AUTO_SERVER=TRUE", - Props.get("db.user"), Props.get("db.password")) + val driver = + Props.mode match { + case Props.RunModes.Production | Props.RunModes.Staging | Props.RunModes.Development => Props.get("db.driver") openOr "org.h2.Driver" + case _ => "org.h2.Driver" + } + val vendor = + Props.mode match { + case Props.RunModes.Production | Props.RunModes.Staging | Props.RunModes.Development => + new StandardDBVendor(driver, + Props.get("db.url") openOr "jdbc:h2:lift_proto.db;AUTO_SERVER=TRUE", + Props.get("db.user"), Props.get("db.password")) + case _ => + new StandardDBVendor( + driver, + "jdbc:h2:mem:OBPTest;DB_CLOSE_DELAY=-1", + Empty, Empty) + } logger.debug("Using database driver: " + driver) LiftRules.unloadHooks.append(vendor.closeAllConnections_! _) DB.defineConnectionManager(DefaultConnectionIdentifier, vendor) } - Mailer.authenticator = for { + Mailer.authenticator = for { user <- Props.get("mail.username") - pass <- Props.get("mail.password") + pass <- Props.get("mail.password") } yield new Authenticator { - override def getPasswordAuthentication = - new PasswordAuthentication(user,pass) - } + override def getPasswordAuthentication = + new PasswordAuthentication(user,pass) + } + + val runningMode = Props.mode match { + case Props.RunModes.Production => "Production mode" + case Props.RunModes.Staging => "Staging mode" + case Props.RunModes.Development => "Development mode" + case Props.RunModes.Test => "test mode" + case _ => "other mode" + } + + logger.info("running mode: " + runningMode) // Use Lift's Mapper ORM to populate the database // you don't need to use Mapper to use Lift... use // any ORM you want - Schemifier.schemify(true, Schemifier.infoF _, OBPUser, Privilege) + Schemifier.schemify(true, Schemifier.infoF _, OBPUser, Privilege, Admin) // where to search snippet LiftRules.addToPackages("code") // For some restful stuff - LiftRules.statelessDispatchTable.append(OBPRest) // stateless -- no session created - - //OAuth API call - LiftRules.dispatch.append(OAuthHandshake) - LiftRules.statelessDispatchTable.append(OAuthHandshake) + LiftRules.statelessDispatchTable.append(v1_0.OBPAPI1_0) + LiftRules.statelessDispatchTable.append(v1_1.OBPAPI1_1) + LiftRules.statelessDispatchTable.append(v1_2.OBPAPI1_2) + LiftRules.statelessDispatchTable.append(ImporterAPI) + LiftRules.statelessDispatchTable.append(CashAccountAPI) + LiftRules.statelessDispatchTable.append(BankMockAPI) - //OAuth Mapper + //OAuth API call + LiftRules.statelessDispatchTable.append(OAuthHandshake) + + //OAuth Mapper Schemifier.schemify(true, Schemifier.infoF _, Nonce) Schemifier.schemify(true, Schemifier.infoF _, Token) - Schemifier.schemify(true, Schemifier.infoF _, Consumer) + Schemifier.schemify(true, Schemifier.infoF _, Consumer) Schemifier.schemify(true, Schemifier.infoF _, HostedAccount) - //lunch the scheduler to clean the database from the expired tokens and nonces - Schedule.schedule(()=> OAuthHandshake.dataBaseCleaner, 2 minutes) - + //launch the scheduler to clean the database from the expired tokens and nonces + Schedule.schedule(()=> OAuthAuthorisation.dataBaseCleaner, 2 minutes) + def check(bool: Boolean) : Box[LiftResponse] = { if(bool){ Empty @@ -115,91 +138,94 @@ class Boot extends Loggable{ Full(PlainTextResponse("unauthorized")) } } - - def getTransactionsAndView (URLParameters : List[String]) : Box[(List[ModeratedTransaction], View)] = + + def getTransactionsAndView (URLParameters : List[String]) : Box[(List[ModeratedTransaction], View, BankAccount)] = { val bank = URLParameters(0) val account = URLParameters(1) val viewName = URLParameters(2) - - val transactionsAndView : Box[(List[ModeratedTransaction], View)] = for { - b <- BankAccount(bank, account) ?~ {"account " + account + " not found for bank " + bank} - v <- View.fromUrl(viewName) ?~ {"view " + viewName + " not found for account " + account + " and bank " + bank} - if(b.authorisedAccess(v, OBPUser.currentUser)) - } yield (b.getModeratedTransactions(v.moderate), v) + + val transactionsAndView : Box[(List[ModeratedTransaction], View, BankAccount)] = for { + b <- BankAccount(bank, account) ?~ {"account " + account + " not found for bank " + bank} + v <- View.fromUrl(viewName) ?~ {"view " + viewName + " not found for account " + account + " and bank " + bank} + transactions <- b.getModeratedTransactions(OBPUser.currentUser, v) + } yield (transactions, v, b) transactionsAndView match { - case Failure(msg, _, _) => logger.info("Could not get transactions and view: " + msg) + case Failure(msg, _, _) => logger.warn("Could not get transactions and view: " + msg) case _ => //don't log anything } transactionsAndView } - - def getAccount(URLParameters : List[String]) = + + def getAccount(URLParameters : List[String]) = { val bankUrl = URLParameters(0) val accountUrl = URLParameters(1) val account = for { - account <- LocalStorage.getAccount(bankUrl,accountUrl) ?~ {"account " + accountUrl + " not found for bank " + bankUrl} + bank <- HostedBank.find("permalink",bankUrl) + account <- bank.getAccount(accountUrl) user <- OBPUser.currentUser ?~ {"user not found when attempting to access account " + account + " of bank " + bankUrl} - bankAccount <- BankAccount(bankUrl, accountUrl) ?~ {"account " + account + " not found for bank " + bankUrl} - if(user.hasMangementAccess(bankAccount)) + if(user.hasMangementAccess(Account toBankAccount account)) } yield account - + account match { case Failure(msg, _, _) => logger.info("Could not get account: " + msg) case _ => //don't log anything } account } - def getTransaction(URLParameters : List[String]) = + def getTransaction(URLParameters : List[String]) = { - if(URLParameters.length==4) - { + if(URLParameters.length==4){ val bank = URLParameters(0) val account = URLParameters(1) val transactionID = URLParameters(2) val viewName = URLParameters(3) val transaction = for{ - bankAccount <- BankAccount(bank, account) ?~ {"account " + account + " not found for bank " + bank} - transaction <- bankAccount.transaction(transactionID) ?~ {"transaction " + transactionID + " not found in account " + account + " for bank " + bank} - view <- View.fromUrl(viewName) ?~ {"view " + viewName + " not found"} - if(bankAccount.authorisedAccess(view, OBPUser.currentUser)) - } yield (view.moderate(transaction),view) - - transaction match { - case Failure(msg, _, _) => logger.info("Could not get transaction: " + msg) - case _ => //don't log anything - } - transaction + bankAccount <- BankAccount(bank, account) ?~ {"account " + account + " not found for bank " + bank} + view <- View.fromUrl(viewName) + transaction <- bankAccount.moderatedTransaction(transactionID, view, OBPUser.currentUser) + } yield (transaction,view) + + transaction match { + case Failure(msg, _, _) => logger.info("Could not get transaction: " + msg) + case _ => //don't log anything + } + + transaction } else Empty - } + } // Build SiteMap val sitemap = List( Menu.i("Home") / "index", Menu.i("Privilege Admin") / "admin" / "privilege" >> TestAccess(() => { check(OBPUser.loggedIn_?) - }) >> LocGroup("admin") + }) >> LocGroup("admin") submenus(Privilege.menus : _*), - Menu.i("OAuth") / "oauth" / "authorize", //OAuth authorization page - Menu.i("Connect") / "connect", + Menu.i("Consumer Admin") / "admin" / "consumers" >> LocGroup("admin") + submenus(Consumer.menus : _*), + Menu("Consumer Registration", "Developers") / "consumer-registration", + Menu.i("Metrics") / "metrics", + Menu.i("OAuth") / "oauth" / "authorize", //OAuth authorization page + Menu.i("Connect") / "connect", Menu.i("Banks") / "banks", //no test => list of open banks //list of open banks (banks with a least a bank account with an open account) Menu.param[Bank]("Bank", "bank", LocalStorage.getBank _ , bank => bank.id ) / "banks" / * , //list of open accounts in a specific bank - Menu.param[Bank]("Accounts", "accounts", LocalStorage.getBank _ , bank => bank.id ) / "banks" / * / "accounts", - + Menu.param[Bank]("Accounts", "accounts", LocalStorage.getBank _ , bank => bank.id ) / "banks" / * / "accounts", + //test if the bank exists and if the user have access to management page Menu.params[Account]("Management", "management", getAccount _ , t => List("")) / "banks" / * / "accounts" / * / "management", - - Menu.params[(List[ModeratedTransaction], View)]("Bank Account", "bank accounts", getTransactionsAndView _ , t => List("") ) + + Menu.params[(List[ModeratedTransaction], View, BankAccount)]("Bank Account", "bank accounts", getTransactionsAndView _ , t => List("") ) / "banks" / * / "accounts" / * / *, - Menu.params[(ModeratedTransaction,View)]("transaction", "transaction", getTransaction _ , t => List("") ) - / "banks" / * / "accounts" / * / "transactions" / * / * + Menu.params[(ModeratedTransaction,View)]("transaction", "transaction", getTransaction _ , t => List("") ) + / "banks" / * / "accounts" / * / "transactions" / * / * ) def sitemapMutators = OBPUser.sitemapMutator @@ -213,7 +239,7 @@ class Boot extends Loggable{ //Show the spinny image when an Ajax call starts LiftRules.ajaxStart = Full(() => LiftRules.jsArtifacts.show("ajax-loader").cmd) - + // Make the spinny image go away when it ends LiftRules.ajaxEnd = Full(() => LiftRules.jsArtifacts.hide("ajax-loader").cmd) @@ -226,32 +252,33 @@ class Boot extends Loggable{ // Use HTML5 for rendering LiftRules.htmlProperties.default.set((r: Req) => - new Html5Properties(r.userAgent)) + new Html5Properties(r.userAgent)) + + LiftRules.explicitlyParsedSuffixes = Helpers.knownSuffixes &~ (Set("com")) // Make a transaction span the whole HTTP request S.addAround(DB.buildLoanWrapper) - + TableSorter.init - /** * A temporary measure to make sure there is an owner for the account, so that someone can set permissions */ - Account.find(("holder", "Music Pictures Limited")) match{ - case Full(a) => + Account.find(("holder", "MUSIC PICTURES LIMITED")) match{ + case Full(a) => HostedAccount.find(By(HostedAccount.accountID,a.id.toString)) match { - case Empty => { - val hostedAccount = HostedAccount.create.accountID(a.id.toString).saveMe + case Empty => { + val hostedAccount = HostedAccount.create.accountID(a.id.toString).saveMe logger.debug("Creating tesobe account user and granting it owner permissions") //create one // val randomPassword = StringHelpers.randomString(12) // println ("The admin password is :"+randomPassword ) val userEmail = "tesobe@tesobe.com" val firstName = "tesobe first name" - val lastName = "tesobe last name" + val lastName = "tesobe last name" val theUserOwner = OBPUser.find(By(OBPUser.email, userEmail)).getOrElse(OBPUser.create.email(userEmail).password("123tesobe456").validated(true).firstName(firstName).lastName(lastName).saveMe) - Privilege.create.account(hostedAccount).ownerPermission(true).user(theUserOwner).saveMe - } - case Full(hostedAccount) => + Privilege.create.account(hostedAccount).ownerPermission(true).user(theUserOwner).saveMe + } + case Full(hostedAccount) => Privilege.find(By(Privilege.account,hostedAccount), By(Privilege.ownerPermission, true)) match{ case Empty => { //create one @@ -263,14 +290,14 @@ class Boot extends Loggable{ val theUserOwner = OBPUser.find(By(OBPUser.email, userEmail)).getOrElse(OBPUser.create.email(userEmail).password("123tesobe456").validated(true).firstName(firstName).lastName(lastName).saveMe) Privilege.create.account(hostedAccount).ownerPermission(true) .mangementPermission(true).authoritiesPermission(true).boardPermission(true) - .teamPermission(true).ourNetworkPermission(true).user(theUserOwner).saveMe + .teamPermission(true).ourNetworkPermission(true).user(theUserOwner).saveMe } case _ => logger.debug("Owner privilege already exists") } case _ => None - } + } case _ => logger.debug("No account found") } - + } } diff --git a/MavLift/src/main/scala/code/api/OBPAPI1.0.scala b/MavLift/src/main/scala/code/api/OBPAPI1.0.scala new file mode 100644 index 000000000..47985230e --- /dev/null +++ b/MavLift/src/main/scala/code/api/OBPAPI1.0.scala @@ -0,0 +1,405 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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 code.actors.EnvelopeInserter +import net.liftweb.http._ +import net.liftweb.http.rest._ +import net.liftweb.json.JsonDSL._ +import net.liftweb.json.Printer._ +import net.liftweb.json.Extraction._ +import net.liftweb.json.JsonAST._ +import java.util.Calendar +import net.liftweb.common.Failure +import net.liftweb.common.Full +import net.liftweb.common.Empty +import net.liftweb.mongodb._ +import net.liftweb.json.JsonAST.JString +import com.mongodb.casbah.Imports._ +import _root_.java.math.MathContext +import org.bson.types._ +import org.joda.time.{ DateTime, DateTimeZone } +import java.util.regex.Pattern +import _root_.net.liftweb.common._ +import _root_.net.liftweb.util._ +import _root_.net.liftweb.http._ +import _root_.net.liftweb.mapper._ +import _root_.net.liftweb.util.Helpers._ +import _root_.net.liftweb.sitemap._ +import _root_.scala.xml._ +import _root_.net.liftweb.http.S._ +import _root_.net.liftweb.http.RequestVar +import _root_.net.liftweb.util.Helpers._ +import _root_.net.liftweb.common.Full +import net.liftweb.mongodb.{ Skip, Limit } +import _root_.net.liftweb.http.S._ +import _root_.net.liftweb.mapper.view._ +import com.mongodb._ +import code.model.dataAccess.{ Account, OBPEnvelope, OBPUser,APIMetric, HostedAccount, LocalStorage} +import code.model.{ModeratedTransaction, ModeratedBankAccount, View, BankAccount, Public, Bank, User} +import code.model.dataAccess.OBPEnvelope._ +import java.util.Date +import code.api.OAuthHandshake._ +import net.liftweb.util.Helpers.now +import net.liftweb.json.Extraction +import _root_.net.liftweb.json.Serialization +import net.liftweb.json.NoTypeHints + + case class APICallAmount( + url: String, + amount: Int + ) + case class APICallAmounts( + stats : List[APICallAmount] + ) + + + case class APICallsForDay( + amount : Int, + date : Date + ) + case class APICallsPerDay( + stats : List[APICallsForDay] + ) + +object OBPAPI1_0 extends RestHelper with Loggable { + + implicit val _formats = Serialization.formats(NoTypeHints) + + val dateFormat = ModeratedTransaction.dateFormat + + private def getOBPUser(httpCode : Int, tokenID : Box[String]) : Box[OBPUser] = + if(httpCode==200) + { + import code.model.Token + Token.find(By(Token.key, tokenID.get)) match { + case Full(token) => tryo{ + token.userId.get.toLong + } match { + case Full(id) => OBPUser.find(By(OBPUser.id, id)) + case _ => Empty + } + case _ => Empty + } + } + else + Empty + + private def logAPICall = + APIMetric.createRecord. + url(S.uriAndQueryString.getOrElse("")). + date((now: TimeSpan)). + save + + serve("obp" / "v1.0" prefix { + + case Nil JsonGet json => { + //log the API call + logAPICall + + 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 bankAlias :: "accounts" :: accountAlias :: "transactions" :: 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(bankAlias, accountAlias) + view <- View.fromUrl(viewName) + transactions <- bankAccount.getModeratedTransactions(getOBPUser(httpCode,oAuthParameters.get("oauth_token")), view, params : _*) + } yield { + JsonResponse("transactions" -> transactions.map(t => t.toJson(view))) + } + + response getOrElse InMemoryResponse(data.getBytes, headers, Nil, 401) : LiftResponse + } + + case bankAlias :: "accounts" :: accountAlias :: "transactions" :: + transactionID :: "transaction" :: viewName :: Nil JsonGet json => { + + //log the API call + logAPICall + + val (httpCode, data, oAuthParameters) = validator("protectedResource", "GET") + val user = getOBPUser(httpCode,oAuthParameters.get("oauth_token")) + + val moderatedTransactionAndView = for { + bank <- Bank(bankAlias) ?~ { "bank " + bankAlias + " not found"} ~> 404 + account <- BankAccount(bankAlias, accountAlias) ?~ { "account " + accountAlias + " not found for bank"} ~> 404 + view <- View.fromUrl(viewName) ?~ { "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 bankAlias :: "accounts" :: accountAlias :: "transactions" :: + transactionID :: "comments" :: viewName :: Nil JsonGet json => { + + //log the API call + logAPICall + + val (httpCode, data, oAuthParameters) = validator("protectedResource", "GET") + val user = getOBPUser(httpCode,oAuthParameters.get("oauth_token")) + + val comments = for { + bank <- Bank(bankAlias) ?~ { "bank " + bankAlias + " not found"} ~> 404 + account <- BankAccount(bankAlias, accountAlias) ?~ { "account " + accountAlias + " not found for bank"} ~> 404 + view <- View.fromUrl(viewName) ?~ { "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 bankPermalink :: "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 = getOBPUser(httpCode,oAuthParameters.get("oauth_token")) + + def bankAccountSet2JsonResponse(bankAccounts: Set[BankAccount]): LiftResponse = { + val accJson = bankAccounts.map(bAcc => bAcc.overviewJson(user)) + JsonResponse(("accounts" -> accJson)) + } + + Bank(bankPermalink) match { + case Full(bank) => + { + if(httpCode == 200) + { + bank.accounts(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 " + bankPermalink + " not found" + InMemoryResponse(error.getBytes(), headers, Nil, 404) + } + } + } + + case bankAlias :: "accounts" :: accountAlias :: "account" :: 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 = getOBPUser(httpCode,oAuthParameters.get("oauth_token")) + + case class ModeratedAccountAndViews(account: ModeratedBankAccount, views: Set[View]) + + val moderatedAccountAndViews = for { + bank <- Bank(bankAlias) ?~ { "bank " + bankAlias + " not found"} ~> 404 + account <- BankAccount(bankAlias, accountAlias) ?~ { "account " + accountAlias + " not found for bank"} ~> 404 + view <- View.fromUrl(viewName) ?~ { "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" -> { "/" + bankAlias + "/accounts/" + accountAlias + "/transactions/" + view.permalink }) ~ + ("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 bankAlias :: "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 bankAlias :: "bank" :: Nil JsonGet json => { + + //log the API call + logAPICall + + def links = { + def accounts = { + ("rel" -> "accounts") ~ + ("href" -> {"/" + bankAlias + "/accounts"}) ~ + ("method" -> "GET") ~ + ("title" -> "Get list of accounts available") + } + + def offices = { + ("rel" -> "offices") ~ + ("href" -> {"/" + bankAlias + "/offices"}) ~ + ("method" -> "GET") ~ + ("title" -> "Get list of offices") + } + + List(accounts, offices) + } + + val bank = for { + bank <- Bank(bankAlias) ?~ { "bank " + bankAlias + " 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)) + } + }) + + // metrics API calls + + serve("obp" / "v1.0" / "metrics" prefix { + case "demo-bar" :: Nil JsonGet json => { + def byURL(metric : APIMetric) : String = + metric.url.get + + def byUsage(x : APICallAmount, y : APICallAmount) = + x.amount > y.amount + + val results = APICallAmounts(APIMetric.findAll.groupBy[String](byURL).toSeq.map(t => APICallAmount(t._1,t._2.length)).toList.sort(byUsage)) + + JsonResponse(Extraction.decompose(results)) + } + + case "demo-line" :: Nil JsonGet json => { + + def byDay(metric : APIMetric) : Date = { + val metricDate = metric.date.get + val cal = Calendar.getInstance() + cal.setTime(metricDate) + cal.set(Calendar.HOUR,0) + cal.set(Calendar.MINUTE,0) + cal.set(Calendar.SECOND,0) + cal.set(Calendar.MILLISECOND,0) + cal.getTime + } + + def byOldestDate(x : APICallsForDay, y : APICallsForDay) : Boolean = + x.date before y.date + + val results = APICallsPerDay(APIMetric.findAll.groupBy[Date](byDay).toSeq.map(t => APICallsForDay(t._2.length,t._1)).toList.sort(byOldestDate)) + JsonResponse(Extraction.decompose(results)) + } + + }) +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/api/OBPAPI1.1.scala b/MavLift/src/main/scala/code/api/OBPAPI1.1.scala new file mode 100644 index 000000000..29cff2e1c --- /dev/null +++ b/MavLift/src/main/scala/code/api/OBPAPI1.1.scala @@ -0,0 +1,1873 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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_1 + +import net.liftweb.http._ +import net.liftweb.http.rest._ +import net.liftweb.json.JsonDSL._ +import net.liftweb.json.Printer._ +import net.liftweb.json.Extraction +import net.liftweb.json.JsonAST._ +import net.liftweb.common.{Failure,Full,Empty, Box, Loggable} +import net.liftweb.mongodb._ +import com.mongodb.casbah.Imports._ +import _root_.java.math.MathContext +import org.bson.types._ +import org.joda.time.{ DateTime, DateTimeZone } +import java.util.regex.Pattern +import _root_.net.liftweb.util._ +import _root_.net.liftweb.mapper._ +import _root_.net.liftweb.util.Helpers._ +import _root_.net.liftweb.sitemap._ +import _root_.scala.xml._ +import _root_.net.liftweb.http.S._ +import _root_.net.liftweb.http.RequestVar +import _root_.net.liftweb.util.Helpers._ +import net.liftweb.mongodb.{ Skip, Limit } +import _root_.net.liftweb.http.S._ +import _root_.net.liftweb.mapper.view._ +import com.mongodb._ +import code.model._ +import java.util.Date +import code.api.OAuthHandshake._ +import code.model.dataAccess.APIMetric +import code.model.dataAccess.OBPEnvelope.{OBPOrder, OBPLimit, OBPOffset, OBPOrdering, OBPFromDate, OBPToDate, OBPQueryParam} +import java.net.URL + +case class TagJSON( + value : String, + posted_date : Date +) + +case class NarrativeJSON( + narrative : String +) + +case class CommentJSON( + value : String, + posted_date : Date +) + +case class ImageJSON( + URL : String, + label : String +) +case class MoreInfoJSON( + more_info : String +) +case class UrlJSON( + URL : String +) +case class ImageUrlJSON( + image_URL : String +) +case class OpenCorporatesUrlJSON( + open_corporates_url : String +) +case class WhereTagJSON( + where : GeoCord +) +case class CorporateLocationJSON( + corporate_location : GeoCord +) +case class PhysicalLocationJSON( + physical_location : GeoCord +) +case class GeoCord( + longitude : Double, + latitude : Double +) +case class ErrorMessage( + error : String +) + +case class SuccessMessage( + success : String +) + +object OBPAPI1_1 extends RestHelper with Loggable { + + implicit def errorToJson(error: ErrorMessage): JValue = Extraction.decompose(error) + implicit def successToJson(success: SuccessMessage): JValue = Extraction.decompose(success) + + val dateFormat = ModeratedTransaction.dateFormat + + private def httpMethod : String = + S.request match { + case Full(r) => r.request.method + case _ => "GET" + } + + private def getUser(httpCode : Int, tokenID : Box[String]) : Box[User] = + if(httpCode==200) + { + import code.model.Token + logger.info("OAuth header correct ") + Token.find(By(Token.key, tokenID.get)) match { + case Full(token) => { + logger.info("access token: "+ token + " found") + val user = User.findById(token.userId.get) + //just a log + user match { + case Full(u) => logger.info("user " + u.emailAddress + " was found from the oauth token") + case _ => logger.info("no user was found for the oauth token") + } + user + } + case _ =>{ + logger.warn("no token " + tokenID.get + " found") + Empty + } + } + } + else + Empty + + private def isThereAnOAuthHeader : Boolean = { + S.request match { + case Full(a) => a.header("Authorization") match { + case Full(parameters) => parameters.contains("OAuth") + case _ => false + } + case _ => false + } + } + + private def logAPICall = + APIMetric.createRecord. + url(S.uriAndQueryString.getOrElse("")). + date((now: TimeSpan)). + save + + private def isFieldAlreadySet(field : String) : Box[String] = + if(field.isEmpty) + Full(field) + else + Failure("field already set, use PUT method to update it") + + private def transactionJson(t : ModeratedTransaction) : JObject = { + ("transaction" -> + ("uuid" -> t.UUID) ~ + ("id" -> t.id) ~ + ("this_account" -> t.bankAccount.map(thisAccountJson)) ~ + ("other_account" -> t.otherBankAccount.map(otherAccountToJson)) ~ + ("details" -> + ("type" -> t.transactionType.getOrElse("")) ~ + ("label" -> t.label.getOrElse("")) ~ + ("posted" -> t.dateOption2JString(t.startDate)) ~ + ("completed" -> t.dateOption2JString(t.finishDate)) ~ + ("new_balance" -> + ("currency" -> t.currency.getOrElse("")) ~ + ("amount" -> t.balance)) ~ + ("value" -> + ("currency" -> t.currency.getOrElse("")) ~ + ("amount" -> t.amount)))) + } + + private def thisAccountJson(thisAccount : ModeratedBankAccount) : JObject = { + ("holder" -> thisAccount.owners.flatten.map(ownerJson)) ~ + ("number" -> thisAccount.number.getOrElse("")) ~ + ("kind" -> thisAccount.accountType.getOrElse("")) ~ + ("bank" -> + ("IBAN" -> thisAccount.iban.getOrElse("")) ~ + ("national_identifier" -> thisAccount.nationalIdentifier.getOrElse("")) ~ + ("name" -> thisAccount.bankName.getOrElse("")) + ) + } + + private def ownerJson(owner : AccountOwner) : JObject = { + ("name" -> owner.name) ~ + ("is_alias" -> false) + } + + private def otherAccountToJson(otherAccount : ModeratedOtherBankAccount) : JObject = { + ("holder" -> + ("name" -> otherAccount.label.display) ~ + ("is_alias" -> otherAccount.isAlias) + ) ~ + ("number" -> otherAccount.number.getOrElse("")) ~ + ("kind" -> otherAccount.kind.getOrElse("")) ~ + ("bank" -> + ("IBAN" -> otherAccount.iban.getOrElse("")) ~ + ("national_identifier" -> otherAccount.nationalIdentifier.getOrElse("")) ~ + ("name" -> otherAccount.bankName.getOrElse("")) + ) + } + + private def userToJson(user : Box[User]) : JValue = + user match { + case Full(u) => + ("id" -> u.id_) ~ + ("provider" -> u.provider ) ~ + ("display_name" -> {u.theFirstName + " " + u.theLastName}) + + case _ => ("id" -> "") ~ + ("provider" -> "") ~ + ("display_name" -> "") + } + + private def oneFieldJson(key : String, value : String) : JObject = + (key -> value) + + private def geoTagToJson(name : String, geoTag : GeoTag) : JValue = { + (name -> + ("latitude" -> geoTag.latitude) ~ + ("longitude" -> geoTag.longitude) ~ + ("date" -> geoTag.datePosted.toString) ~ + ("user" -> userToJson(geoTag.postedBy)) + ) + } + + private def geoTagToJson(name : String, geoTag : Option[GeoTag]) : JValue = { + geoTag match { + case Some(tag) => + (name -> + ("latitude" -> tag.latitude) ~ + ("longitude" -> tag.longitude) ~ + ("date" -> tag.datePosted.toString) ~ + ("user" -> userToJson(tag.postedBy)) + ) + case _ => "" + } + } + + private def moderatedTransactionMetadata(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : Box[ModeratedTransactionMetadata] = + for { + account <- BankAccount(bankId, accountId) ?~ { "bank " + bankId + " and account " + accountId + " not found for bank"} + view <- View.fromUrl(viewId) ?~ { "view " + viewId + " not found"} + moderatedTransaction <- account.moderatedTransaction(transactionID, view, user) ?~ "view/transaction not authorized" + metadata <- Box(moderatedTransaction.metadata) ?~ {"view " + viewId + " does not authorize metadata access"} + } yield metadata + + private def moderatedTransactionOtherAccount(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : Box[ModeratedOtherBankAccount] = + for { + account <- BankAccount(bankId, accountId) ?~ { "bank " + bankId + " and account " + accountId + " not found for bank"} + view <- View.fromUrl(viewId) ?~ { "view " + viewId + " not found"} + moderatedTransaction <- account.moderatedTransaction(transactionID, view, user) ?~ "view/transaction not authorized" + otherAccount <- Box(moderatedTransaction.otherBankAccount) ?~ {"view " + viewId + " does not authorize other account access"} + } yield otherAccount + + private def moderatedOtherAccount(bankId : String, accountId : String, viewId : String, other_account_ID : String, user : Box[User]) : Box[ModeratedOtherBankAccount] = + for { + account <- BankAccount(bankId, accountId) ?~ { "bank " + bankId + " and account " + accountId + " not found for bank"} + view <- View.fromUrl(viewId) ?~ { "view " + viewId + " not found"} + moderatedOtherBankAccount <- account.moderatedOtherBankAccount(other_account_ID, view, user) + } yield moderatedOtherBankAccount + + private def moderatedOtherAccountMetadata(bankId : String, accountId : String, viewId : String, other_account_ID : String, user : Box[User]) : Box[ModeratedOtherBankAccountMetadata] = + for { + moderatedOtherBankAccount <- moderatedOtherAccount(bankId, accountId, viewId, other_account_ID, user) + metadata <- Box(moderatedOtherBankAccount.metadata) ?~! {"view " + viewId + "does not allow other bank account metadata access"} + } yield metadata + + serve("obp" / "v1.1" prefix { + + case Nil JsonGet json => { + logAPICall + + 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.1") ~ + ("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 "banks" :: Nil JsonGet json => { + logAPICall + def bankToJson( b : Bank) = { + ("bank" -> + ("id" -> b.permalink) ~ + ("short_name" -> b.shortName) ~ + ("full_name" -> b.fullName) ~ + ("logo" -> b.logoURL) ~ + ("website" -> b.website) + ) + } + + JsonResponse("banks" -> Bank.all.map(bankToJson _ )) + } + + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: Nil JsonGet json => { + logAPICall + + def bankToJson( b : Bank) = { + ("bank" -> + ("id" -> b.permalink) ~ + ("short_name" -> b.shortName) ~ + ("full_name" -> b.fullName) ~ + ("logo" -> b.logoURL) ~ + ("website" -> b.website) + ) + } + + for { + b <- Bank(bankId) + } yield JsonResponse(bankToJson(b)) + } + }) + + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: Nil JsonGet json => { + //log the API call + logAPICall + + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + val user = getUser(httpCode,oAuthParameters.get("oauth_token")) + + def viewToJson(v : View) : JObject = { + ("view" -> ( + ("id" -> v.permalink) ~ + ("short_name" -> v.name) ~ + ("description" -> v.description) ~ + ("is_public" -> v.isPublic) + )) + } + + def accountToJson(acc : BankAccount, user : Box[User]) : JObject = { + //just a log + user match { + case Full(u) => logger.info("user " + u.emailAddress + " was found") + case _ => logger.info("no user was found") + } + + val views = acc permittedViews user + ("account" -> ( + ("id" -> acc.permalink) ~ + ("views_available" -> views.map(viewToJson(_))) + )) + } + def bankAccountSet2JsonResponse(bankAccounts: Set[BankAccount]): LiftResponse = { + val accJson = bankAccounts.map(accountToJson(_,user)) + JsonResponse(("accounts" -> accJson)) + } + + Bank(bankId) match { + case Full(bank) => + { + if(isThereAnOAuthHeader) + { + if(httpCode == 200) + { + bank.accounts(user) match { + case Full(a) => bankAccountSet2JsonResponse(a.toSet) + case _ => InMemoryResponse("no account found".getBytes, Nil, Nil, 404) + } + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + { + bank.accounts(user) match { + case Full(a) => bankAccountSet2JsonResponse(a.toSet) + case _ => InMemoryResponse("no account found".getBytes, Nil, Nil, 404) + } + } + } + case _ => { + val error = "bank " + bankId + " not found" + JsonResponse(ErrorMessage(error), Nil, Nil, httpCode) + } + } + } + }) + + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "account" :: Nil JsonGet json => { + logAPICall + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + val headers = ("Content-type" -> "application/x-www-form-urlencoded") :: Nil + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + + case class ModeratedAccountAndViews(account: ModeratedBankAccount, views: Set[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(viewId) ?~ { "view " + viewId + " not found for account" } ~> 404 + moderatedAccount <- account.moderatedBankAccount(view, user) ?~ { "view/account not authorized" } ~> 401 + availableViews <- Full(account.permittedViews(user)) + } yield ModeratedAccountAndViews(moderatedAccount, availableViews) + + val bankName = moderatedAccountAndViews.flatMap(_.account.bankName) getOrElse "" + + def viewJson(view: View): JObject = { + + val isPublic: Boolean = + view match { + case Public => true + case _ => false + } + + ("id" -> view.id) ~ + ("short_name" -> view.name) ~ + ("description" -> view.description) ~ + ("is_public" -> isPublic) + } + + def ownerJson(accountOwner: AccountOwner): JObject = { + ("user_id" -> accountOwner.id) ~ + ("user_provider" -> bankName) ~ + ("display_name" -> accountOwner.name) + } + + def balanceJson(account: ModeratedBankAccount): JObject = { + ("currency" -> account.currency.getOrElse("")) ~ + ("amount" -> account.balance) + } + + def json(account: ModeratedBankAccount, views: Set[View]): JObject = { + ("account" -> + ("number" -> account.number.getOrElse("")) ~ + ("owners" -> account.owners.flatten.map(ownerJson)) ~ + ("type" -> account.accountType.getOrElse("")) ~ + ("balance" -> balanceJson(account)) ~ + ("IBAN" -> account.iban.getOrElse("")) ~ + ("views_available" -> views.map(viewJson)) + ) + } + if(isThereAnOAuthHeader) + { + if(httpCode == 200) + moderatedAccountAndViews.map(mv => JsonResponse(json(mv.account, mv.views))) + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + moderatedAccountAndViews.map(mv => JsonResponse(json(mv.account, mv.views))) + } + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: Nil JsonGet json => { + //log the API call + logAPICall + + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + 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 + + def transactionsJson(transactions : List[ModeratedTransaction], v : View) : JObject = { + ("transactions" -> transactions.map(transactionJson)) + } + val response : Box[JsonResponse] = for { + bankAccount <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + transactions <- bankAccount.getModeratedTransactions(getUser(httpCode,oAuthParameters.get("oauth_token")), view, params: _*) + } yield { + JsonResponse(transactionsJson(transactions, view),Nil, Nil, 200) + } + + response getOrElse (JsonResponse(ErrorMessage(message), Nil, Nil, 401)) : LiftResponse + } + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "transaction" :: Nil JsonGet json => { + //log the API call + logAPICall + + def transactionInJson(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : JsonResponse = { + val moderatedTransaction = for { + account <- BankAccount(bankId, accountId) ?~ { "bank " + bankId + " and account " + accountId + " not found for bank"} + view <- View.fromUrl(viewId) ?~ { "view " + viewId + " not found"} + moderatedTransaction <- account.moderatedTransaction(transactionID, view, user) ?~ "view/transaction not authorized" + } yield moderatedTransaction + + moderatedTransaction match { + case Full(transaction) => JsonResponse(transactionJson(transaction), Nil, Nil, 200) + case Failure(msg,_,_) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode,oAuthParameters.get("oauth_token")) + transactionInJson(bankId, accountId, viewId, transactionID, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, 400) + } + else + transactionInJson(bankId, accountId, viewId, transactionID, None) + } + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "narrative" :: Nil JsonGet json => { + //log the API call + logAPICall + + def narrativeInJson(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : JsonResponse = { + val narrative = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,user) + narrative <- Box(metadata.ownerComment) ?~ {"view " + viewId + " does not authorize narrative access"} + } yield narrative + + narrative match { + case Full(narrative) => JsonResponse(oneFieldJson("narrative", narrative), Nil, Nil, 200) + case Failure(msg,_,_) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode,oAuthParameters.get("oauth_token")) + narrativeInJson(bankId, accountId, viewId, transactionID, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, 400) + } + else + narrativeInJson(bankId, accountId, viewId, transactionID, None) + } + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "narrative" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + tryo{ + json.extract[NarrativeJSON] + } match { + case Full(narrativeJson) => { + + val user = getUser(httpCode,oAuthParameters.get("oauth_token")) + + val addNarrativeFunc = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,user) + narrative <- Box(metadata.ownerComment) ?~ {"view " + viewId + " does not authorize narrative access"} + narrativeSetted <- isFieldAlreadySet(narrative) + addNarrativeFunc <- Box(metadata.addOwnerComment) ?~ {"view " + viewId + " does not authorize narrative edit"} + } yield addNarrativeFunc + + addNarrativeFunc match { + case Full(addNarrative) => { + addNarrative(narrativeJson.narrative) + JsonResponse(SuccessMessage("narrative successfully saved"), Nil, Nil, 201) + } + case Failure(msg,_,_) => JsonResponse(ErrorMessage(msg), Nil, Nil, 400) + case _ => JsonResponse(ErrorMessage("error"), Nil, Nil, 400) + } + } + case _ => JsonResponse(ErrorMessage("wrong JSON format"), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "narrative" :: Nil JsonPut json -> _ => { + //log the API call + logAPICall + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + tryo{ + json.extract[NarrativeJSON] + } match { + case Full(narrativeJson) => { + + val user = getUser(httpCode,oAuthParameters.get("oauth_token")) + + val addNarrativeFunc = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,user) + narrative <- Box(metadata.ownerComment) ?~ {"view " + viewId + " does not authorize narrative access"} + addNarrativeFunc <- Box(metadata.addOwnerComment) ?~ {"view " + viewId + " does not authorize narrative edit"} + } yield addNarrativeFunc + + addNarrativeFunc match { + case Full(addNarrative) => { + addNarrative(narrativeJson.narrative) + JsonResponse(SuccessMessage("narrative successfully saved"), Nil, Nil, 201) + } + case Failure(msg,_,_) => JsonResponse(ErrorMessage(msg), Nil, Nil, 400) + case _ => JsonResponse(ErrorMessage("error"), Nil, Nil, 400) + } + } + case _ => JsonResponse(ErrorMessage("wrong JSON format"), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "comments" :: Nil JsonGet json => { + //log the API call + logAPICall + + def commentToJson(comment : code.model.Comment) : JValue = { + ("comment" -> + ("id" -> comment.id_) ~ + ("date" -> comment.datePosted.toString) ~ + ("value" -> comment.text) ~ + ("user" -> userToJson(comment.postedBy)) ~ + ("reply_to" -> comment.replyToID) + ) + } + + def commentsToJson(comments : List[code.model.Comment]) : JValue = { + ("comments" -> comments.map(commentToJson)) + } + + def commentsResponce(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : JsonResponse = { + val comments = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,user) + comments <- Box(metadata.comments) ?~ {"view " + viewId + " does not authorize comments access"} + } yield comments + + comments match { + case Full(commentsList) => JsonResponse(commentsToJson(commentsList), Nil, Nil, 200) + case Failure(msg,_,_) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode,oAuthParameters.get("oauth_token")) + commentsResponce(bankId, accountId, viewId, transactionID, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, 400) + } + else + commentsResponce(bankId, accountId, viewId, transactionID, None) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "comments" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + tryo{ + json.extract[CommentJSON] + } match { + case Full(commentJson) => { + def addComment(user : User, viewID : Long, text: String, datePosted : Date) = { + val addComment = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,Full(user)) + addCommentFunc <- Box(metadata.addComment) ?~ {"view " + viewId + " does not authorize adding comment"} + } yield addCommentFunc + + addComment.map( + func =>{ + func(user.id_, viewID, text, datePosted) + Full(text) + } + ) + } + + val comment = for{ + user <- getUser(httpCode,oAuthParameters.get("oauth_token")) ?~ "User not found. Authentication via OAuth is required" + view <- View.fromUrl(viewId) ?~ {"view " + viewId +" view not found"} + postedComment <- addComment(user, view.id, commentJson.value, commentJson.posted_date) + } yield postedComment + + comment match { + case Full(text) => JsonResponse(SuccessMessage("comment : " + text + "successfully saved"), Nil, Nil, 201) + case Failure(msg, _, _) => JsonResponse(ErrorMessage(msg), Nil, Nil, 400) + case _ => JsonResponse(ErrorMessage("error"), Nil, Nil, 400) + } + } + case _ => JsonResponse(ErrorMessage("wrong JSON format"), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "tags" :: Nil JsonGet json => { + //log the API call + logAPICall + + def tagToJson(tag : Tag) : JValue = { + ("tag" -> + ("id" -> tag.id_) ~ + ("date" -> tag.datePosted.toString) ~ + ("value" -> tag.value) ~ + ("user" -> userToJson(tag.postedBy)) + ) + } + + def tagsToJson(tags : List[Tag]) : JValue = { + ("tags" -> tags.map(tagToJson)) + } + + def tagsResponce(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : JsonResponse = { + val tags = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,user) + tags <- Box(metadata.tags) ?~ {"view " + viewId + " does not authorize tags access"} + } yield tags + + tags match { + case Full(tagsList) => JsonResponse(tagsToJson(tagsList), Nil, Nil, 200) + case Failure(msg,_,_) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode,oAuthParameters.get("oauth_token")) + tagsResponce(bankId, accountId, viewId, transactionID, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, 400) + } + else + tagsResponce(bankId, accountId, viewId, transactionID, None) + } + }) + serve("obp" / "v1.1" prefix { + //post a tag + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "tags" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + tryo{ + json.extract[TagJSON] + } match { + case Full(tagJson) => { + if(! tagJson.value.contains(" ")) + { + def addTag(user : User, viewID : Long, tag: String, datePosted : Date) = { + val addTag = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,Full(user)) + addTagFunc <- Box(metadata.addTag) ?~ {"view " + viewId + " does not authorize adding comment"} + } yield addTagFunc + + addTag.map( + func =>{ + Full(func(user.id_, viewID, tag, datePosted)) + } + ) + } + + val tag = for{ + user <- getUser(httpCode,oAuthParameters.get("oauth_token")) ?~ "User not found. Authentication via OAuth is required" + view <- View.fromUrl(viewId) ?~ {"view " + viewId +" view not found"} + postedTagID <- addTag(user, view.id, tagJson.value, tagJson.posted_date) + } yield postedTagID + + tag match { + case Full(postedTagID) => JsonResponse(SuccessMessage("tag : " + postedTagID + "successfully saved"), Nil, Nil, 201) + case Failure(msg, _, _) => JsonResponse(ErrorMessage(msg), Nil, Nil, 400) + case _ => JsonResponse(ErrorMessage("error"), Nil, Nil, 400) + } + } + else + { + JsonResponse(ErrorMessage("tag value MUST NOT contain a white space"), Nil, Nil, 400) + } + } + case _ => JsonResponse(ErrorMessage("wrong JSON format"), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "images" :: Nil JsonGet json => { + //log the API call + logAPICall + + def imageToJson(image : TransactionImage) : JValue = { + ("image" -> + ("id" -> image.id_) ~ + ("label" -> image.description) ~ + ("URL" -> image.imageUrl.toString) ~ + ("date" -> image.datePosted.toString) ~ + ("user" -> userToJson(image.postedBy)) + ) + } + + def imagesToJson(images : List[TransactionImage]) : JValue = { + ("images" -> images.map(imageToJson)) + } + + def imagesResponce(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : JsonResponse = { + val images = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,user) + images <- Box(metadata.images) ?~ {"view " + viewId + " does not authorize tags access"} + } yield images + + images match { + case Full(imagesList) => JsonResponse(imagesToJson(imagesList), Nil, Nil, 200) + case Failure(msg,_,_) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode,oAuthParameters.get("oauth_token")) + imagesResponce(bankId, accountId, viewId, transactionID, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, 400) + } + else + imagesResponce(bankId, accountId, viewId, transactionID, None) + } + }) + serve("obp" / "v1.1" prefix { + //post an image + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "images" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + tryo{ + json.extract[ImageJSON] + } match { + case Full(imageJson) => { + def addImage(user : User, viewID : Long, label: String, url : URL) : Box[String] = { + val addImage = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,Full(user)) + addImageFunc <- Box(metadata.addImage) ?~ {"view " + viewId + " does not authorize adding comment"} + } yield addImageFunc + + addImage.map( + func =>{ + val datePosted = (now: TimeSpan) + func(user.id_, viewID, label, datePosted, url).id_ + } + ) + } + + val imageId = for{ + user <- getUser(httpCode,oAuthParameters.get("oauth_token")) ?~ "User not found. Authentication via OAuth is required" + view <- View.fromUrl(viewId) ?~ {"view " + viewId +" view not found"} + url <- tryo{new URL(imageJson.URL)} ?~! "Could not parse url string as a valid URL" + postedImageId <- addImage(user, view.id, imageJson.label, url) + } yield postedImageId + + imageId match { + case Full(postedImageId) => JsonResponse(SuccessMessage("image : " + postedImageId + "successfully saved"), Nil, Nil, 201) + case Failure(msg, _, _) => JsonResponse(ErrorMessage(msg), Nil, Nil, 400) + case _ => JsonResponse(ErrorMessage("error"), Nil, Nil, 400) + } + } + case _ => JsonResponse(ErrorMessage("wrong JSON format"), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "where" :: Nil JsonGet json => { + //log the API call + logAPICall + + def whereTagResponce(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : JsonResponse = { + val whereTag = for { + metadata <- moderatedTransactionMetadata(bankId,accountId,viewId,transactionID,user) + whereTag <- Box(metadata.whereTag) ?~ {"view " + viewId + " does not authorize tags access"} + } yield whereTag + + whereTag match { + case Full(whereTag) => JsonResponse(geoTagToJson("where", whereTag), Nil, Nil, 200) + case Failure(msg,_,_) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + whereTagResponce(bankId, accountId, viewId, transactionID, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, 400) + } + else + whereTagResponce(bankId, accountId, viewId, transactionID, None) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankID :: "accounts" :: accountID :: viewId :: "transactions" :: transactionID :: "metadata" :: "where" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + tryo{ + json.extract[WhereTagJSON] + } match { + case Full(whereTagJson) => { + def addWhereTag(user : User, viewID : Long, longitude: Double, latitude : Double) : Box[Boolean] = { + val addWhereTag = for { + metadata <- moderatedTransactionMetadata(bankID,accountID,viewId,transactionID,Full(user)) + addWhereTagFunc <- Box(metadata.addWhereTag) ?~ {"view " + viewId + " does not authorize adding where tag"} + } yield addWhereTagFunc + + addWhereTag.map( + func =>{ + val datePosted = (now: TimeSpan) + func(user.id_, viewID, datePosted, longitude, latitude) + } + ) + } + + val postedGeoTag = for{ + user <- getUser(httpCode,oAuthParameters.get("oauth_token")) ?~ "User not found. Authentication via OAuth is required" + view <- View.fromUrl(viewId) ?~ {"view " + viewId +" view not found"} + posterWheteTag <- addWhereTag(user, view.id, whereTagJson.where.longitude, whereTagJson.where.latitude) + } yield posterWheteTag + + postedGeoTag match { + case Full(postedWhereTag) => + if(postedWhereTag) + JsonResponse(SuccessMessage("Geo tag successfully saved"), Nil, Nil, 201) + else + JsonResponse(ErrorMessage("Geo tag could not be saved"), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(ErrorMessage(msg), Nil, Nil, 400) + case _ => JsonResponse(ErrorMessage("error"), Nil, Nil, 400) + } + } + case _ => JsonResponse(ErrorMessage("wrong JSON format"), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankID :: "accounts" :: accountID :: viewId :: "transactions" :: transactionID :: "metadata" :: "where" :: Nil JsonPut json -> _ => { + //log the API call + logAPICall + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + tryo{ + json.extract[WhereTagJSON] + } match { + case Full(whereTagJson) => { + def addWhereTag(user : User, viewID : Long, longitude: Double, latitude : Double) : Box[Boolean] = { + val addWhereTag = for { + metadata <- moderatedTransactionMetadata(bankID,accountID,viewId,transactionID,Full(user)) + addWhereTagFunc <- Box(metadata.addWhereTag) ?~ {"view " + viewId + " does not authorize adding where tag"} + } yield addWhereTagFunc + + addWhereTag.map( + func =>{ + val datePosted = (now: TimeSpan) + func(user.id_, viewID, datePosted, longitude, latitude) + } + ) + } + + val postedGeoTag = for{ + user <- getUser(httpCode,oAuthParameters.get("oauth_token")) ?~ "User not found. Authentication via OAuth is required" + view <- View.fromUrl(viewId) ?~ {"view " + viewId +" view not found"} + posterWheteTag <- addWhereTag(user, view.id, whereTagJson.where.longitude, whereTagJson.where.latitude) + } yield posterWheteTag + + postedGeoTag match { + case Full(postedWhereTag) => + if(postedWhereTag) + JsonResponse(SuccessMessage("Geo tag successfully saved"), Nil, Nil, 201) + else + JsonResponse(ErrorMessage("Geo tag could not be saved"), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(ErrorMessage(msg), Nil, Nil, 400) + case _ => JsonResponse(ErrorMessage("error"), Nil, Nil, 400) + } + } + case _ => JsonResponse(ErrorMessage("wrong JSON format"), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "other_account" :: Nil JsonGet json => { + //log the API call + logAPICall + + def otherAccountToJson(otherAccount : ModeratedOtherBankAccount) : JObject = { + ("id" -> otherAccount.id) ~ + ("number" -> otherAccount.number.getOrElse("")) ~ + ("holder" -> + ("name" -> otherAccount.label.display) ~ + ("is_alias" -> otherAccount.isAlias) + ) ~ + ("national_identifier" -> otherAccount.nationalIdentifier.getOrElse("")) ~ + ("IBAN" -> otherAccount.iban.getOrElse("")) ~ + ("bank_name" -> otherAccount.bankName.getOrElse("")) ~ + ("swift_bic" -> otherAccount.swift_bic.getOrElse("")) + } + + def otherAccountResponce(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : JsonResponse = { + moderatedTransactionOtherAccount(bankId,accountId,viewId,transactionID,user) match { + case Full(otherAccount) => JsonResponse(otherAccountToJson(otherAccount), Nil, Nil, 200) + case Failure(msg,_,_) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + otherAccountResponce(bankId, accountId, viewId, transactionID, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, 400) + } + else + otherAccountResponce(bankId, accountId, viewId, transactionID, None) + } + }) + serve("obp" / "v1.1" prefix { + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: other_account_ID :: "metadata" :: Nil JsonGet json => { + //log the API call + logAPICall + + def otherAccountMetadataToJson(metadata : ModeratedOtherBankAccountMetadata) : JObject = { + ("more_info" -> metadata.moreInfo.getOrElse("")) ~ + ("URL" -> metadata.url.getOrElse("")) ~ + ("image_URL" -> metadata.imageURL.getOrElse("")) ~ + ("open_corporates_URL" -> metadata.openCorporatesURL.getOrElse("")) ~ + ("corporate_location" -> geoTagToJson("corporate_location",metadata.corporateLocation)) ~ + ("physical_location" -> geoTagToJson("physical_location",metadata.physicalLocation)) + } + + def otherAccountMetadataResponce(bankId : String, accountId : String, viewId : String, other_account_ID : String, user : Box[User]) : JsonResponse = { + val otherAccountMetaData = for{ + otherAccount <- moderatedOtherAccount(bankId, accountId, viewId, other_account_ID, user) + metaData <- Box(otherAccount.metadata) ?~! {"view " + viewId + "does not allow other account metadata access" } + } yield metaData + + otherAccountMetaData match { + case Full(metadata) => JsonResponse(otherAccountMetadataToJson(metadata), Nil, Nil, 200) + case Failure(msg,_,_) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + otherAccountMetadataResponce(bankId, accountId, viewId, other_account_ID, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, 400) + } + else + otherAccountMetadataResponce(bankId, accountId, viewId, other_account_ID, None) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "more_info" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + def postMoreInfoResponce(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[MoreInfoJSON] + } match { + case Full(moreInfoJson) => { + + def addMoreInfo(bankId : String, accountId : String, viewId : String, otherAccountId : String, user : Box[User], moreInfo : String): Box[Boolean] = { + val addMoreInfo = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,user) + moreInfo <- Box(metadata.moreInfo) ?~! {"view " + viewId + " does not authorize access to more_info"} + setMoreInfo <- isFieldAlreadySet(moreInfo) + addMoreInfo <- Box(metadata.addMoreInfo) ?~ {"view " + viewId + " does not authorize adding more_info"} + } yield addMoreInfo + + addMoreInfo.map( + func =>{ + func(moreInfo) + } + ) + } + + addMoreInfo(bankId, accountId, viewId, otherAccountId, user, moreInfoJson.more_info) match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("more info successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("more info could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + postMoreInfoResponce(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + postMoreInfoResponce(bankId, accountId, viewId, otherAccountId, Empty) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "more_info" :: Nil JsonPut json -> _ => { + //log the API call + logAPICall + + def updateMoreInfoResponce(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[MoreInfoJSON] + } match { + case Full(moreInfoJson) => { + + def addMoreInfo(bankId : String, accountId : String, viewId : String, otherAccountId : String, user : Box[User], moreInfo : String): Box[Boolean] = { + val addMoreInfo = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,user) + addMoreInfo <- Box(metadata.addMoreInfo) ?~ {"view " + viewId + " does not authorize adding more_info"} + } yield addMoreInfo + + addMoreInfo.map( + func =>{ + func(moreInfo) + } + ) + } + + addMoreInfo(bankId, accountId, viewId, otherAccountId, user, moreInfoJson.more_info) match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("more info successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("more info could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + updateMoreInfoResponce(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + updateMoreInfoResponce(bankId, accountId, viewId, otherAccountId, Empty) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "url" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + def postURLResponce(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[UrlJSON] + } match { + case Full(urlJson) => { + + def addUrl(bankId : String, accountId : String, viewId : String, otherAccountId : String, user : Box[User], url : String): Box[Boolean] = { + val addUrl = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,user) + url <- Box(metadata.url) ?~! {"view " + viewId + " does not authorize access to URL"} + setUrl <- isFieldAlreadySet(url) + addUrl <- Box(metadata.addURL) ?~ {"view " + viewId + " does not authorize adding URL"} + } yield addUrl + + addUrl.map( + func =>{ + func(url) + } + ) + } + + addUrl(bankId, accountId, viewId, otherAccountId, user, urlJson.URL) match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("URL successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("URL could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + postURLResponce(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + postURLResponce(bankId, accountId, viewId, otherAccountId, Empty) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "url" :: Nil JsonPut json -> _ => { + //log the API call + logAPICall + + def updateURLResponce(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[UrlJSON] + } match { + case Full(urlJson) => { + + def addUrl(bankId : String, accountId : String, viewId : String, otherAccountId : String, user : Box[User], url : String): Box[Boolean] = { + val addUrl = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,user) + addUrl <- Box(metadata.addURL) ?~ {"view " + viewId + " does not authorize adding URL"} + } yield addUrl + + addUrl.map( + func =>{ + func(url) + } + ) + } + + addUrl(bankId, accountId, viewId, otherAccountId, user, urlJson.URL) match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("URL successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("URL could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + updateURLResponce(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + updateURLResponce(bankId, accountId, viewId, otherAccountId, Empty) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "image_url" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + def postImageUrlResponce(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[ImageUrlJSON] + } match { + case Full(imageUrlJson) => { + + def addImageUrl(bankId : String, accountId : String, viewId : String, otherAccountId : String, user : Box[User], url : String): Box[Boolean] = { + val addImageUrl = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,user) + imageUrl <- Box(metadata.imageURL) ?~! {"view " + viewId + " does not authorize access to image URL"} + setImageUrl <- isFieldAlreadySet(imageUrl) + addImageUrl <- Box(metadata.addImageURL) ?~ {"view " + viewId + " does not authorize adding image URL"} + } yield addImageUrl + + addImageUrl.map( + func =>{ + func(url) + } + ) + } + + addImageUrl(bankId, accountId, viewId, otherAccountId, user, imageUrlJson.image_URL) match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("Image URL successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("Image URL could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + postImageUrlResponce(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + postImageUrlResponce(bankId, accountId, viewId, otherAccountId, Empty) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "image_url" :: Nil JsonPut json -> _ => { + //log the API call + logAPICall + + def updateImageUrlResponce(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[ImageUrlJSON] + } match { + case Full(imageUrlJson) => { + + def addImageUrl(bankId : String, accountId : String, viewId : String, otherAccountId : String, user : Box[User], url : String): Box[Boolean] = { + val addImageUrl = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,user) + addImageUrl <- Box(metadata.addImageURL) ?~ {"view " + viewId + " does not authorize adding image URL"} + } yield addImageUrl + + addImageUrl.map( + func =>{ + func(url) + } + ) + } + + addImageUrl(bankId, accountId, viewId, otherAccountId, user, imageUrlJson.image_URL) match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("Image URL successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("Image URL could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + updateImageUrlResponce(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + updateImageUrlResponce(bankId, accountId, viewId, otherAccountId, Empty) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "open_corporates_url" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + def postOpenCorporatesUrlResponce(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[OpenCorporatesUrlJSON] + } match { + case Full(openCorporatesUrlJSON) => { + + def addOpenCorporatesUrl(bankId : String, accountId : String, viewId : String, otherAccountId : String, user : Box[User], url : String): Box[Boolean] = { + val addOpenCorporatesUrl = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,user) + openCorporatesUrl <- Box(metadata.openCorporatesURL) ?~! {"view " + viewId + " does not authorize access to open_corporates_url"} + setImageUrl <- isFieldAlreadySet(openCorporatesUrl) + addOpenCorporatesUrl <- Box(metadata.addOpenCorporatesURL) ?~ {"view " + viewId + " does not authorize adding open_corporates_url"} + } yield addOpenCorporatesUrl + + addOpenCorporatesUrl.map( + func =>{ + func(url) + } + ) + } + + addOpenCorporatesUrl(bankId, accountId, viewId, otherAccountId, user, openCorporatesUrlJSON.open_corporates_url) match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("open_corporates_url successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("open_corporates_url could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + postOpenCorporatesUrlResponce(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + postOpenCorporatesUrlResponce(bankId, accountId, viewId, otherAccountId, Empty) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "open_corporates_url" :: Nil JsonPut json -> _ => { + //log the API call + logAPICall + + def updateOpenCorporatesURL(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[OpenCorporatesUrlJSON] + } match { + case Full(openCorporatesUrlJSON) => { + + def addOpenCorporatesUrl(bankId : String, accountId : String, viewId : String, otherAccountId : String, user : Box[User], url : String): Box[Boolean] = { + val addOpenCorporatesUrl = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,user) + addOpenCorporatesUrl <- Box(metadata.addOpenCorporatesURL) ?~ {"view " + viewId + " does not authorize adding open_corporates_url"} + } yield addOpenCorporatesUrl + + addOpenCorporatesUrl.map( + func =>{ + func(url) + } + ) + } + + addOpenCorporatesUrl(bankId, accountId, viewId, otherAccountId, user, openCorporatesUrlJSON.open_corporates_url) match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("open_corporates_url successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("open_corporates_url could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + updateOpenCorporatesURL(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + updateOpenCorporatesURL(bankId, accountId, viewId, otherAccountId, Empty) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "corporate_location" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + def postCorporateLocation(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[CorporateLocationJSON] + } match { + case Full(corporateLocationJSON) => { + + def addCorporateLocation(user : User, viewID : Long, longitude: Double, latitude : Double) : Box[Boolean] = { + val addCorporateLocation = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,Full(user)) + addCorporateLocation <- Box(metadata.addCorporateLocation) ?~ {"view " + viewId + " does not authorize adding corporate_location"} + } yield addCorporateLocation + + addCorporateLocation.map( + func =>{ + val datePosted = (now: TimeSpan) + func(user.id_, viewID, datePosted, longitude, latitude) + } + ) + } + val postedGeoTag = for { + u <- user ?~ "User not found. Authentication via OAuth is required" + view <- View.fromUrl(viewId) ?~ {"view " + viewId +" view not found"} + postedGeoTag <- addCorporateLocation(u, view.id, corporateLocationJSON.corporate_location.longitude, corporateLocationJSON.corporate_location.latitude) + } yield postedGeoTag + + postedGeoTag match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("corporate_location successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("corporate_location could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + postCorporateLocation(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "corporate_location" :: Nil JsonPut json -> _ => { + //log the API call + logAPICall + + def postCorporateLocation(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[CorporateLocationJSON] + } match { + case Full(corporateLocationJSON) => { + + def addCorporateLocation(user : User, viewID : Long, longitude: Double, latitude : Double) : Box[Boolean] = { + val addCorporateLocation = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,Full(user)) + addCorporateLocation <- Box(metadata.addCorporateLocation) ?~ {"view " + viewId + " does not authorize adding corporate_location"} + } yield addCorporateLocation + + addCorporateLocation.map( + func =>{ + val datePosted = (now: TimeSpan) + func(user.id_, viewID, datePosted, longitude, latitude) + } + ) + } + val postedGeoTag = for { + u <- user ?~ "User not found. Authentication via OAuth is required" + view <- View.fromUrl(viewId) ?~ {"view " + viewId +" view not found"} + postedGeoTag <- addCorporateLocation(u, view.id, corporateLocationJSON.corporate_location.longitude, corporateLocationJSON.corporate_location.latitude) + } yield postedGeoTag + + postedGeoTag match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("corporate_location successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("corporate_location could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + postCorporateLocation(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "physical_location" :: Nil JsonPost json -> _ => { + //log the API call + logAPICall + + def postPhysicalLocation(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[PhysicalLocationJSON] + } match { + case Full(physicalLocationJSON) => { + + def addPhysicalLocation(user : User, viewID : Long, longitude: Double, latitude : Double) : Box[Boolean] = { + val addPhysicalLocation = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,Full(user)) + addPhysicalLocation <- Box(metadata.addPhysicalLocation) ?~ {"view " + viewId + " does not authorize adding physical_location"} + } yield addPhysicalLocation + + addPhysicalLocation.map( + func =>{ + val datePosted = (now: TimeSpan) + func(user.id_, viewID, datePosted, longitude, latitude) + } + ) + } + val postedGeoTag = for { + u <- user ?~ "User not found. Authentication via OAuth is required" + view <- View.fromUrl(viewId) ?~ {"view " + viewId +" view not found"} + postedGeoTag <- addPhysicalLocation(u, view.id, physicalLocationJSON.physical_location.longitude, physicalLocationJSON.physical_location.latitude) + } yield postedGeoTag + + postedGeoTag match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("physical_location successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("physical_location could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + postPhysicalLocation(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) + serve("obp" / "v1.1" prefix{ + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: otherAccountId :: "metadata" :: "physical_location" :: Nil JsonPut json -> _ => { + //log the API call + logAPICall + + def postPhysicalLocation(bankId : String, accountId : String, viewId : String, otherAccountId: String, user : Box[User]) : JsonResponse = + tryo{ + json.extract[PhysicalLocationJSON] + } match { + case Full(physicalLocationJSON) => { + + def addPhysicalLocation(user : User, viewID : Long, longitude: Double, latitude : Double) : Box[Boolean] = { + val addPhysicalLocation = for { + metadata <- moderatedOtherAccountMetadata(bankId,accountId,viewId,otherAccountId,Full(user)) + addPhysicalLocation <- Box(metadata.addPhysicalLocation) ?~ {"view " + viewId + " does not authorize adding physical_location"} + } yield addPhysicalLocation + + addPhysicalLocation.map( + func =>{ + val datePosted = (now: TimeSpan) + func(user.id_, viewID, datePosted, longitude, latitude) + } + ) + } + val postedGeoTag = for { + u <- user ?~ "User not found. Authentication via OAuth is required" + view <- View.fromUrl(viewId) ?~ {"view " + viewId +" view not found"} + postedGeoTag <- addPhysicalLocation(u, view.id, physicalLocationJSON.physical_location.longitude, physicalLocationJSON.physical_location.latitude) + } yield postedGeoTag + + postedGeoTag match { + case Full(posted) => + if(posted) + JsonResponse(Extraction.decompose(SuccessMessage("physical_location successfully saved")), Nil, Nil, 201) + else + JsonResponse(Extraction.decompose(ErrorMessage("physical_location could not be saved")), Nil, Nil, 500) + case Failure(msg, _, _) => JsonResponse(Extraction.decompose(ErrorMessage(msg)), Nil, Nil, 400) + case _ => JsonResponse(Extraction.decompose(ErrorMessage("error")), Nil, Nil, 400) + } + } + case _ => JsonResponse(Extraction.decompose(ErrorMessage("wrong JSON format")), Nil, Nil, 400) + } + + + if(isThereAnOAuthHeader) + { + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + if(httpCode == 200) + { + val user = getUser(httpCode, oAuthParameters.get("oauth_token")) + postPhysicalLocation(bankId, accountId, viewId, otherAccountId, user) + } + else + JsonResponse(ErrorMessage(message), Nil, Nil, httpCode) + } + else + JsonResponse(ErrorMessage("Authentication via OAuth is required"), Nil, Nil, 400) + } + }) +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/api/OBPRestHelper.scala b/MavLift/src/main/scala/code/api/OBPRestHelper.scala new file mode 100644 index 000000000..c6b7ae2f0 --- /dev/null +++ b/MavLift/src/main/scala/code/api/OBPRestHelper.scala @@ -0,0 +1,87 @@ +package code.api + +import net.liftweb.http.rest.RestHelper +import net.liftweb.http.Req +import net.liftweb.common._ +import net.liftweb.http.LiftResponse +import net.liftweb.http.JsonResponse +import code.util.APIUtil._ +import code.model.User +import code.api.OAuthHandshake._ + +class OBPRestHelper extends RestHelper with Loggable { + + implicit def jsonResponseBoxToJsonReponse(box: Box[JsonResponse]): JsonResponse = { + box match { + case Full(r) => r + case Failure(msg, _, _) => { + logger.info("API Failure: " + msg) + errorJsonResponse(msg) + } + case _ => errorJsonResponse() + } + } + + def failIfBadOauth(fn: (Box[User]) => Box[JsonResponse]) : JsonResponse = { + if (isThereAnOAuthHeader) { + getUser match { + case Full(u) => fn(Full(u)) + case Failure(msg, _, _) => errorJsonResponse(msg) + case _ => errorJsonResponse("oauth error") + } + } else fn(Empty) + } + + class RichStringList(list: List[String]) { + val listLen = list.length + + /** + * Normally we would use ListServeMagic's prefix function, but it works with PartialFunction[Req, () => Box[LiftResponse]] + * instead of the PartialFunction[Req, Box[User] => Box[JsonResponse]] that we need. This function does the same thing, really. + */ + def oPrefix(pf: PartialFunction[Req, Box[User] => Box[JsonResponse]]): PartialFunction[Req, Box[User] => Box[JsonResponse]] = + new PartialFunction[Req, Box[User] => Box[JsonResponse]] { + def isDefinedAt(req: Req): Boolean = + req.path.partPath.startsWith(list) && { + pf.isDefinedAt(req.withNewPath(req.path.drop(listLen))) + } + + def apply(req: Req): Box[User] => Box[JsonResponse] = + pf.apply(req.withNewPath(req.path.drop(listLen))) + } + } + + //Give all lists of strings in OBPRestHelpers the oPrefix method + implicit def stringListToRichStringList(list : List[String]) : RichStringList = new RichStringList(list) + + def oauthServe(handler : PartialFunction[Req, Box[User] => Box[JsonResponse]]) : Unit = { + val obpHandler : PartialFunction[Req, () => Box[LiftResponse]] = { + new PartialFunction[Req, () => Box[LiftResponse]] { + def apply(r : Req) = { + failIfBadOauth { + handler(r) + } + } + def isDefinedAt(r : Req) = handler.isDefinedAt(r) + } + } + serve(obpHandler) + } + + override protected def serve(handler: PartialFunction[Req, () => Box[LiftResponse]]) : Unit= { + + val obpHandler : PartialFunction[Req, () => Box[LiftResponse]] = { + new PartialFunction[Req, () => Box[LiftResponse]] { + def apply(r : Req) = { + //Wraps the partial function with some logging + logAPICall + handler(r) + } + def isDefinedAt(r : Req) = handler.isDefinedAt(r) + } + } + super.serve(obpHandler) + } + + +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/api/bankmock.scala b/MavLift/src/main/scala/code/api/bankmock.scala new file mode 100644 index 000000000..66955af6f --- /dev/null +++ b/MavLift/src/main/scala/code/api/bankmock.scala @@ -0,0 +1,172 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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 + +import net.liftweb.common.{Box,Full,Loggable} +import net.liftweb.mapper.By +import net.liftweb.http.S +import net.liftweb.http.rest.RestHelper +import net.liftweb.util.Props +import net.liftweb.http.JsonResponse +import net.liftweb.json.Extraction +import net.liftweb.json.JsonAST.JValue +import code.model.Token +import code.model.TokenType +import net.liftweb.util.Helpers.tryo +import net.liftweb.json.Serialization +import net.liftweb.json.NoTypeHints + +case class ErrorMessage( + error : String +) + +case class SuccessMessage( + success : String +) + +case class TokenValidity( + isValid : Boolean +) + +case class ApplicationInformation( + name : String, + callbackURL : String +) + +case class Verifier( + value : String +) + +case class UserData( + id : String +) + +/** +* this object provides the API call required for the Bank mock, +* They are required during the User authentication / Application Authorization steps +* that the Bank Mock need to handle as part of the OAuth 1.0 protocol authentication. +*/ +object BankMockAPI extends RestHelper with Loggable { + + implicit def errorToJson(error: ErrorMessage): JValue = Extraction.decompose(error) + implicit def successToJson(success: SuccessMessage): JValue = Extraction.decompose(success) + implicit def TokenValidityToJson(msg: TokenValidity): JValue = Extraction.decompose(msg) + implicit def applicationInfoToJson(info: ApplicationInformation): JValue = Extraction.decompose(info) + implicit def verifierToJson(verifier: Verifier): JValue = Extraction.decompose(verifier) + + //extract and compare the sent key with the local one (shared secret) + def isValidKey : Boolean = { + val sentKey : Box[String] = + for{ + req <- S.request + sentKey <- req.header("BankMockKey") + } yield sentKey + val localKey : Box[String] = Props.get("BankMockKey") + localKey == sentKey + } + + def requestToken(token : String) : Box[Token] = + Token.find(By(Token.key,token), By(Token.tokenType,TokenType.Request)) + + serve("obp" / "v1.0" prefix { + case "request-token-verification" :: Nil JsonGet _ => { + if(isValidKey) + S.param("oauth_token") match { + case Full(token) => { + requestToken(token) match { + case Full(tkn) => JsonResponse(TokenValidity(tkn.isValid), Nil, Nil, 200) + case _ => JsonResponse(ErrorMessage("invalid or expired request token"), Nil, Nil, 401) + } + } + case _ => JsonResponse(ErrorMessage("No request token"), Nil, Nil, 401) + } + else + JsonResponse(ErrorMessage("No key found or wrong key"), Nil, Nil, 401) + } + + case "application-information" :: Nil JsonGet _ => { + if(isValidKey) + S.param("oauth_token") match { + case Full(token) => { + requestToken(token) match { + case Full(tkn) => + if(tkn.isValid) + tkn.consumerId.foreign match { + case Full(app) => { + val applicationInfo = ApplicationInformation( + name = app.name, + callbackURL = tkn.callbackURL.get + ) + JsonResponse(applicationInfo, Nil, Nil, 200) + } + case _ => JsonResponse(ErrorMessage("Application not found"), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage("Expired request token"), Nil, Nil, 401) + case _ => JsonResponse(ErrorMessage("Request token not found"), Nil, Nil, 401) + } + } + case _ => JsonResponse(ErrorMessage("No request token"), Nil, Nil, 401) + } + else + JsonResponse(ErrorMessage("No key found or wrong key"), Nil, Nil, 401) + } + case "verifiers" :: Nil JsonPost json -> _ => { + if(isValidKey) + S.param("oauth_token") match { + case Full(token) => { + tryo{ + json.extract[UserData] + } match { + case Full(userData) => { + requestToken(token) match { + case Full(tkn) =>{ + //associate the token with the user + tkn.userId(userData.id) + val verifier = tkn.gernerateVerifier + tkn.save + JsonResponse(Verifier(verifier), Nil, Nil, 200) + } + case _ => JsonResponse(ErrorMessage("Request token not found"), Nil, Nil, 401) + } + } + case _ => JsonResponse(ErrorMessage("Wrong JSON format"), Nil, Nil, 401) + } + } + case _ => JsonResponse(ErrorMessage("No OAuth token"), Nil, Nil, 401) + } + else + JsonResponse(ErrorMessage("No key found or wrong key"), Nil, Nil, 401) + } + }) +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/api/cash.scala b/MavLift/src/main/scala/code/api/cash.scala new file mode 100644 index 000000000..fa2e4f9cc --- /dev/null +++ b/MavLift/src/main/scala/code/api/cash.scala @@ -0,0 +1,186 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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 + +import net.liftweb.http._ +import net.liftweb.http.rest._ +import net.liftweb.json.JsonDSL._ +import net.liftweb.json.Printer._ +import net.liftweb.json.Extraction._ +import net.liftweb.json.JsonAST +import JsonAST._ +import net.liftweb.common.{Failure,Full, Empty, Box,Loggable} +import net.liftweb.mongodb._ +import com.mongodb.casbah.Imports._ +import _root_.java.math.MathContext +import org.bson.types._ +import org.joda.time.{ DateTime, DateTimeZone } +import java.util.regex.Pattern +import _root_.net.liftweb.util._ +import _root_.net.liftweb.http._ +import _root_.net.liftweb.mapper._ +import _root_.net.liftweb.util.Helpers._ +import _root_.net.liftweb.sitemap._ +import _root_.scala.xml._ +import _root_.net.liftweb.http.S._ +import _root_.net.liftweb.http.RequestVar +import _root_.net.liftweb.common.Full +import net.liftweb.mongodb.{ Skip, Limit } +import _root_.net.liftweb.http.S._ +import _root_.net.liftweb.mapper.view._ +import com.mongodb._ +import code.model.dataAccess._ +import java.util.Date +import net.liftweb.util.Helpers.now +import net.liftweb.json.Extraction +import _root_.net.liftweb.json.Serialization +import net.liftweb.json.NoTypeHints +import scala.math.BigDecimal._ + +case class CashTransaction( + otherParty : String, + date : Date, + amount : Double, + kind : String, + label : String, + otherInformation : String +) + +object CashAccountAPI extends RestHelper with Loggable { + + implicit def errorToJson(error: ErrorMessage): JValue = Extraction.decompose(error) + implicit def successToJson(msg: SuccessMessage): JValue = Extraction.decompose(msg) + + def isValidKey : Boolean = { + val sentKey : Box[String] = + for{ + req <- S.request + sentKey <- req.header("cashApplicationKey") + } yield sentKey + val localKey : Box[String] = Props.get("cashApplicationKey") + localKey == sentKey + } + + serve("obp" / "v1.0" prefix { + + case "cash-accounts" :: id :: "transactions" :: Nil JsonPost json -> _ => { + if(isValidKey) + Account.find(id) match { + case Full(account) => + tryo{ + json.extract[CashTransaction] + } match { + case Full(cashTransaction) => { + + val thisAccountBank = OBPBank.createRecord. + IBAN(""). + national_identifier(""). + name(account.bankName) + + val thisAccount = OBPAccount.createRecord. + holder(account.holder.get). + number(account.number.get). + kind(account.kind.get). + bank(thisAccountBank) + + val otherAccountBank = OBPBank.createRecord. + IBAN(""). + national_identifier(""). + name("") + + val otherAccount = OBPAccount.createRecord. + holder(cashTransaction.otherParty). + number(""). + kind(""). + bank(otherAccountBank) + + val amount : BigDecimal = { + if(cashTransaction.kind == "in") + BigDecimal(cashTransaction.amount).setScale(2,RoundingMode.HALF_UP).abs + else + BigDecimal((cashTransaction.amount * (-1) )).setScale(2,RoundingMode.HALF_UP) + } + + val newBalance : OBPBalance = OBPBalance.createRecord. + currency(account.currency.get). + amount(account.balance.get + amount) + + val newValue : OBPValue = OBPValue.createRecord. + currency(account.currency.get). + amount(amount) + + val details = OBPDetails.createRecord. + type_en("cash"). + type_de("Bargeld"). + posted(cashTransaction.date). + other_data(cashTransaction.otherInformation). + new_balance(newBalance). + value(newValue). + completed(cashTransaction.date). + label(cashTransaction.label) + + val transaction = OBPTransaction.createRecord. + this_account(thisAccount). + other_account(otherAccount). + details(details) + + val env = OBPEnvelope.createRecord. + obp_transaction(transaction).save + account.balance(account.balance.get + amount).lastUpdate(now) + env.createAliases + env.save + + JsonResponse(SuccessMessage("transaction successfully added"), Nil, Nil, 200) + } + case _ =>{ + val error = "Post data must be in the format: \n" + + pretty( + JsonAST.render( + Extraction.decompose( + CashTransaction( + otherParty = "other party", + date = new Date, + amount = 1231.12, + kind = "in / out", + label = "what is this transaction for", + otherInformation = "more info" + )))) + JsonResponse(ErrorMessage(error), Nil, Nil, 400) + } + } + case _ => JsonResponse(ErrorMessage("Account " + id + " not found" ), Nil, Nil, 400) + } + else + JsonResponse(ErrorMessage("No key found or wrong key"), Nil, Nil, 401) + } + }) +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/api/importer.scala b/MavLift/src/main/scala/code/api/importer.scala new file mode 100644 index 000000000..c39724bbc --- /dev/null +++ b/MavLift/src/main/scala/code/api/importer.scala @@ -0,0 +1,263 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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 + +import code.actors.EnvelopeInserter +import net.liftweb.http._ +import net.liftweb.http.rest._ +import net.liftweb.json.JsonDSL._ +import net.liftweb.json.Printer._ +import net.liftweb.json.Extraction._ +import net.liftweb.json.JsonAST._ +import java.util.Calendar +import net.liftweb.common.Failure +import net.liftweb.common.Full +import net.liftweb.common.Empty +import net.liftweb.mongodb._ +import net.liftweb.json.JsonAST.JString +import com.mongodb.casbah.Imports._ +import _root_.java.math.MathContext +import org.bson.types._ +import org.joda.time.{ DateTime, DateTimeZone } +import java.util.regex.Pattern +import _root_.net.liftweb.common._ +import _root_.net.liftweb.util._ +import _root_.net.liftweb.http._ +import _root_.net.liftweb.mapper._ +import _root_.net.liftweb.util.Helpers._ +import _root_.net.liftweb.sitemap._ +import _root_.scala.xml._ +import _root_.net.liftweb.http.S._ +import _root_.net.liftweb.http.RequestVar +import _root_.net.liftweb.util.Helpers._ +import _root_.net.liftweb.common.Full +import net.liftweb.mongodb.{ Skip, Limit } +import _root_.net.liftweb.http.S._ +import _root_.net.liftweb.mapper.view._ +import com.mongodb._ +import code.model.dataAccess.{ Account, OBPEnvelope, OBPUser, HostedAccount, LocalStorage } +import code.model.{ModeratedTransaction, ModeratedBankAccount, View, BankAccount, Public, Bank, User} +import code.model.dataAccess.OBPEnvelope._ +import java.util.Date +import code.api.OAuthHandshake._ + +object ImporterAPI extends RestHelper with Loggable { + serve { + //a temporary way to add transaction via api for a single specific exception case. should be removed later. + case "api" :: "tmp" :: "transactions" :: Nil JsonPost json => { + val secretKey = S.param("secret") + + def addMatchingTransactions(secret: String) = { + val rawEnvelopes = json._1.children + val envelopes = rawEnvelopes.flatMap(OBPEnvelope.fromJValue) + val matchingEnvelopes = for { + e <- envelopes + bankName <- Props.get("exceptional_account_bankName") + number <- Props.get("exceptional_account_number") + kind <- Props.get("exceptional_account_kind") + if(e.obp_transaction.get.this_account.get.bank.get.name.get == bankName) + if(e.obp_transaction.get.this_account.get.number.get == number) + if(e.obp_transaction.get.this_account.get.kind.get == kind) + } yield e + + val ipAddress = json._2.remoteAddr + logger.info("Received " + rawEnvelopes.size + + " json transactions to insert from ip address " + ipAddress) + logger.info("Received " + matchingEnvelopes.size + + " valid transactions to insert from ip address " + ipAddress) + + /** + * Using an actor to do insertions avoids concurrency issues with + * duplicate transactions by processing transaction batches one + * at a time. We'll have to monitor this to see if non-concurrent I/O + * is too inefficient. If it is, we could break it up into one actor + * per "Account". + */ + val createdEnvelopes = EnvelopeInserter !? (3 seconds, matchingEnvelopes) + + createdEnvelopes match { + case Full(l: List[JObject]) =>{ + if(matchingEnvelopes.size!=0) + { + Account.find(("number" -> Props.get("exceptional_account_number").getOrElse("")) ~ + ("bankName" -> Props.get("exceptional_account_bankName").getOrElse("")) ~ + ("kind" -> Props.get("exceptional_account_kind").getOrElse(""))) + match { + case Full(account) => account.lastUpdate(new Date).save + case _ => + } + } + JsonResponse(JArray(l)) + } + case _ => InternalServerErrorResponse() + } + } + + def valid(secret : String) = { + val authorised = for (validSecret <- Props.get("exceptional_account_secret")) + yield secret == validSecret + + authorised getOrElse false + } + + secretKey match { + case Full(s) => if(valid(s)) + addMatchingTransactions(s) + else + UnauthorizedResponse("wrong secret key") + case _ => NotFoundResponse() + } + + } + } + + serve { + + /** + * curl -i -H "Content-Type: application/json" -X POST -d '{ + * "obp_transaction":{ + * "this_account":{ + * "holder":"Music Pictures Limited", + * "number":"123567", + * "kind":"current", + * "bank":{ + * "IBAN":"DE1235123612", + * "national_identifier":"de.10010010", + * "name":"Postbank" + * } + * }, + * "other_account":{ + * "holder":"Client 1", + * "number":"123567", + * "kind":"current", + * "bank":{ + * "IBAN":"UK12222879", + * "national_identifier":"uk.10010010", + * "name":"HSBC" + * } + * }, + * "details":{ + * "type_en":"Transfer", + * "type_de":"Überweisung", + * "posted":{ + * "$dt":"2012-01-04T18:06:22.000Z" + * }, + * "completed":{ + * "$dt":"2012-09-04T18:52:13.000Z" + * }, + * "new_balance":{ + * "currency":"EUR", + * "amount":"4323.45" + * }, + * "value":{ + * "currency":"EUR", + * "amount":"123.45" + * }, + * "other_data":"9" + * } + * } + * } ' http://localhost:8080/api/transactions + */ + case "api" :: "transactions" :: Nil JsonPost json => { + + // + // WARNING! + // + // If you have not configured a web server to restrict this URL + // appropriately, anyone will be + // able to post transactions to your database. This would obviously + // be undesirable. So you should + // definitely sort that out. + // + // + + val rawEnvelopes = json._1.children + + val envelopes : List[OBPEnvelope]= rawEnvelopes.flatMap(e => { + OBPEnvelope.envlopesFromJvalue(e) + }) + + val ipAddress = json._2.remoteAddr + logger.info("Received " + rawEnvelopes.size + + " json transactions to insert from ip address " + ipAddress) + logger.info("Received " + envelopes.size + + " valid transactions to insert from ip address " + ipAddress) + + /** + * Using an actor to do insertions avoids concurrency issues with + * duplicate transactions by processing transaction batches one + * at a time. We'll have to monitor this to see if non-concurrent I/O + * is too inefficient. If it is, we could break it up into one actor + * per "Account". + */ + val createdEnvelopes = EnvelopeInserter !? (3 seconds, envelopes) + + createdEnvelopes match { + case Full(l: List[JObject]) =>{ + logger.info("inserted " + l.size + "transactions") + if(envelopes.size!=0) + { + //we assume here that all the Envelopes concerns only one account + val accountNumber = envelopes(0).obp_transaction.get.this_account.get.number.get + val bankName = envelopes(0).obp_transaction.get.this_account.get.bank.get.name.get + val accountKind = envelopes(0).obp_transaction.get.this_account.get.kind.get + val holder = envelopes(0).obp_transaction.get.this_account.get.holder.get + //Get all accounts with this account number and kind + val accounts = Account.findAll(("number" -> accountNumber) ~ ("kind" -> accountKind) ~ ("holder" -> holder)) + //Now get the one that actually belongs to the right bank + val wantedAccount = accounts.find(_.bankName == bankName) + wantedAccount match { + case Some(account) => { + def updateAccountBalance() = { + val newest = OBPEnvelope.findAll(("obp_transaction.this_account.number" -> accountNumber) ~ + ("obp_transaction.this_account.kind" -> accountKind) ~ + ("obp_transaction.this_account.bank.name" -> bankName), + ("obp_transaction.details.completed" -> -1), Limit(1)).headOption + if(newest.isDefined) { + logger.debug("Updating current balance for " + bankName + "/" + accountNumber + "/" + accountKind) + account.balance(newest.get.obp_transaction.get.details.get.new_balance.get.amount.get).save + } + else logger.warn("Could not update latest account balance") + } + account.lastUpdate(new Date).save + updateAccountBalance() + } + case _ => logger.info("BankName/accountNumber/kind not found : " + bankName + "/" + accountNumber + "/" + accountKind) + } + } + JsonResponse(JArray(l)) + } + case _ => InternalServerErrorResponse() + } + } + } +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/api/oauth1.0.scala b/MavLift/src/main/scala/code/api/oauth1.0.scala new file mode 100644 index 000000000..110ec7089 --- /dev/null +++ b/MavLift/src/main/scala/code/api/oauth1.0.scala @@ -0,0 +1,502 @@ +/** +Open Bank Project + +Copyright 2011,2012 TESOBE / Music Pictures Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + Open Bank Project (http://www.openbankproject.com) + Copyright 2011,2012 TESOBE / Music Pictures Ltd + + This product includes software developed at + TESOBE (http://www.tesobe.com/) + by + Simon Redfern : simon AT tesobe DOT com + Everett Sochowski: everett AT tesobe DOT com + Ayoub Benali : ayoub AT tesobe Dot com + */ +package code.api +import net.liftweb.http.rest.RestHelper +import net.liftweb.http.Req +import net.liftweb.http.GetRequest +import net.liftweb.http.PostRequest +import net.liftweb.http.LiftResponse +import net.liftweb.common.Box +import net.liftweb.http.InMemoryResponse +import net.liftweb.common.{Full,Empty,Loggable} +import net.liftweb.http.S +import code.model.{Nonce, Consumer, Token} +import net.liftweb.mapper.By +import java.util.Date +import java.net.{URLEncoder, URLDecoder} +import javax.crypto.spec.SecretKeySpec +import javax.crypto.Mac +import net.liftweb.util.Helpers +import code.model.AppType._ +import code.model.TokenType._ +import scala.compat.Platform +import scala.xml.NodeSeq +import Helpers._ +import net.liftweb.util.Props +import code.model.TokenType +import code.model.User +import net.liftweb.common.Failure + +/** +* This object provides the API calls necessary to third party applications +* so they could authenticate their users. +*/ + +object OAuthHandshake extends RestHelper with Loggable { + serve + { + //Handling get request for a "request token" + case Req("oauth" :: "initiate" :: Nil,_ ,PostRequest) => + { + //Extract the OAuth parameters from the header and test if the request is valid + var (httpCode, message, oAuthParameters) = validator("requestToken", "POST") + //Test if the request is valid + if(httpCode==200) + { + //Generate the token and secret + val (token,secret) = generateTokenAndSecret() + //Save the token that we have generated + if(saveRequestToken(oAuthParameters,token, secret)) + message="oauth_token="+token+"&oauth_token_secret="+ + secret+"&oauth_callback_confirmed=true" + } + val headers = ("Content-type" -> "application/x-www-form-urlencoded") :: Nil + //return an HTTP response + Full(InMemoryResponse(message.getBytes,headers,Nil,httpCode)) + } + + case Req("oauth" :: "token" :: Nil,_, PostRequest) => + { + //Extract the OAuth parameters from the header and test if the request is valid + var (httpCode, message, oAuthParameters) = validator("authorizationToken", "POST") + //Test if the request is valid + if(httpCode==200) + { + //Generate the token and secret + val (token,secret) = generateTokenAndSecret() + //Save the token that we have generated + if(saveAuthorizationToken(oAuthParameters,token, secret)) + //remove the request token so the application could not exchange it + //again to get an other access token + Token.find(By(Token.key,oAuthParameters.get("oauth_token").get)) match { + case Full(requestToken) => requestToken.delete_! + case _ => None + } + + message="oauth_token="+token+"&oauth_token_secret="+secret + } + val headers = ("Content-type" -> "application/x-www-form-urlencoded") :: Nil + //return an HTTP response + Full(InMemoryResponse(message.getBytes,headers,Nil,httpCode)) + } + } + + //Check if the request (access toke or request token) is valid and return a tuple + def validator(requestType : String, httpMethod : String) : (Int, String, Map[String,String]) = { + //return a Map containing the OAuth parameters : oauth_prameter -> value + def getAllParameters : Map[String,String]= { + + //Convert the string containing the list of OAuth parameters to a Map + def toMap(parametersList : String) = { + //transform the string "oauth_prameter="value"" + //to a tuple (oauth_parameter,Decoded(value)) + def dynamicListExtract(input: String) = { + val oauthPossibleParameters = + List( + "oauth_consumer_key", + "oauth_nonce", + "oauth_signature_method", + "oauth_timestamp", + "oauth_version", + "oauth_signature", + "oauth_callback", + "oauth_token", + "oauth_verifier" + ) + + if (input contains "=") { + val split = input.split("=",2) + val parameterValue = split(1).replace("\"","") + //add only OAuth parameters and not empty + if(oauthPossibleParameters.contains(split(0)) && ! parameterValue.isEmpty) + Some(split(0),parameterValue) // return key , value + else + None + } + else + None + } + //we delete the "Oauth" prefix and all the white spaces that may exist in the string + val cleanedParameterList = parametersList.stripPrefix("OAuth").replaceAll("\\s","") + Map(cleanedParameterList.split(",").flatMap(dynamicListExtract _): _*) + } + + S.request match { + case Full(a) => a.header("Authorization") match { + case Full(parameters) => toMap(parameters) + case _ => Map(("","")) + } + case _ => Map(("","")) + } + } + //return true if the authorization header has a duplicated parameter + def duplicatedParameters = { + var output=false + val authorizationParameters = S.request.get.header("Authorization").get.split(",") + + //count the iterations of a parameter in the authorization header + def countPram(parameterName : String, parametersArray :Array[String] )={ + var i = 0 + parametersArray.foreach(t => {if (t.split("=")(0) == parameterName) i+=1}) + i + } + + //return true if on of the Authorization header parameter is present more than one time + authorizationParameters.foreach( + t => { + if(countPram(t.split("=")(0),authorizationParameters)>1 && !output) + output=true + } + ) + output + } + + def suportedOAuthVersion(OAuthVersion : Option[String]) : Boolean = { + //auth_version is OPTIONAL. If present, MUST be set to "1.0". + OAuthVersion match + { + case Some(a) => a=="1" || a=="1.0" + case _ => true + } + } + def wrongTimestamp(requestTimestamp : Date) : Boolean= { + val currentTime = Platform.currentTime / 1000 + + val timeRange : Long = Helpers.minutes(3) + //check if the timestamp is positive and in the time range + requestTimestamp.getTime < 0 || requestTimestamp.before(new Date(currentTime - timeRange)) || requestTimestamp.after(new Date(currentTime + timeRange)) + } + + def alReadyUsedNonce(parameters : Map[String, String]) : Boolean = { + + /* + * The nonce value MUST be unique across all requests with the + * same timestamp, client credentials, and token combinations. + */ + val token = parameters.get("oauth_token") getOrElse "" + + Nonce.count( + By(Nonce.value,parameters.get("oauth_nonce").get), + By(Nonce.tokenKey, token), + By(Nonce.consumerkey,parameters.get("oauth_consumer_key").get), + By(Nonce.timestamp, new Date(parameters.get("oauth_timestamp").get.toLong)) + ) !=0 + } + + def registeredApplication(consumerKey : String ) : Boolean = { + Consumer.find(By(Consumer.key,consumerKey)) match { + case Full(application) => application.isActive + case _ => false + } + } + def correctSignature(OAuthparameters : Map[String, String], httpMethod : String) = { + //Normalize an encode the request parameters as explained in Section 3.4.1.3.2 + //of OAuth 1.0 specification (http://tools.ietf.org/html/rfc5849) + def generateOAuthParametersString(OAuthparameters : Map[String, String]) : String = { + def sortParam( keyAndValue1 : (String, String), keyAndValue2 : (String, String))= keyAndValue1._1.compareTo(keyAndValue2._1) < 0 + var parameters ="" + + //sort the parameters by name + OAuthparameters.toList.sortWith(sortParam _).foreach( + t => + if(t._1 != "oauth_signature") + parameters += URLEncoder.encode(t._1,"UTF-8")+"%3D"+ URLEncoder.encode(t._2,"UTF-8")+"%26" + + ) + parameters = parameters.dropRight(3) //remove the "&" encoded sign + parameters + } + + + //prepare the base string + var baseString = httpMethod+"&"+URLEncoder.encode(Props.get("hostname").openOr(S.hostAndPath) + S.uri,"UTF-8")+"&" + baseString+= generateOAuthParametersString(OAuthparameters) + + val encodeBaseString = URLEncoder.encode(baseString,"UTF-8") + //get the key to sign + val comsumer = Consumer.find( + By(Consumer.key,OAuthparameters.get("oauth_consumer_key").get) + ).get + var secret= comsumer.secret.toString + + OAuthparameters.get("oauth_token") match { + case Some(tokenKey) => Token.find(By(Token.key,tokenKey)) match { + case Full(token) => secret+= "&" +token.secret.toString() + case _ => secret+= "&" + } + case _ => secret+= "&" + } + logger.info("base string : " + baseString) + //signing process + val signingAlgorithm : String = if(OAuthparameters.get("oauth_signature_method").get.toLowerCase == "hmac-sha256") + "HmacSHA256" + else + "HmacSHA1" + + logger.info("signing method:" + signingAlgorithm) + logger.info("signing key: " + secret) + logger.info("signing key in bytes: " + secret.getBytes("UTF-8")) + + var m = Mac.getInstance(signingAlgorithm); + m.init(new SecretKeySpec(secret.getBytes("UTF-8"),signingAlgorithm)) + val calculatedSignature = Helpers.base64Encode(m.doFinal(baseString.getBytes)) + + logger.info("calculatedSignature:" + calculatedSignature) + logger.info("received signature:" + OAuthparameters.get("oauth_signature").get) + logger.info("received signature after decoding:" + URLDecoder.decode(OAuthparameters.get("oauth_signature").get)) + + calculatedSignature== URLDecoder.decode(OAuthparameters.get("oauth_signature").get,"UTF-8") + } + + //check if the token exists and is still valid + def validToken(tokenKey : String, verifier : String) ={ + Token.find(By(Token.key, tokenKey),By(Token.tokenType,TokenType.Request)) match { + case Full(token) => + token.isValid && token.verifier == verifier + case _ => false + } + } + + def validToken2(tokenKey : String) = { + Token.find(By(Token.key, tokenKey),By(Token.tokenType,TokenType.Access)) match { + case Full(token) => token.isValid + case _ => false + } + } + + //@return the missing parameters depending of the request type + def missingOauthParameters(parameters : Map[String, String], requestType : String) : Set[String] = { + val parametersBase = + List( + "oauth_consumer_key", + "oauth_nonce", + "oauth_signature_method", + "oauth_timestamp", + "oauth_signature" + ) + if(requestType == "requestToken") + ("oauth_callback" :: parametersBase).toSet diff parameters.keySet + else if(requestType=="authorizationToken") + ("oauth_token" :: "oauth_verifier" :: parametersBase).toSet diff parameters.keySet + else if(requestType=="protectedResource") + ("oauth_token" :: parametersBase).toSet diff parameters.keySet + else + parameters.keySet + } + + def supportedSignatureMethod(oauthSignatureMethod : String ) : Boolean = + { + oauthSignatureMethod.toLowerCase == "hmac-sha256" || + oauthSignatureMethod.toLowerCase == "hmac-sha1" + } + + var message ="" + var httpCode : Int = 500 + + var parameters = getAllParameters + + //does all the OAuth parameters are presents? + val missingParams = missingOauthParameters(parameters,requestType) + if( missingParams.size != 0 ) + { + message = "the following parameters are missing : " + missingParams.mkString(", ") + httpCode = 400 + } + //no parameter exists more than one times + else if (duplicatedParameters) + { + message = "Duplicated oauth protocol parameters" + httpCode = 400 + } + //valid OAuth + else if(!suportedOAuthVersion(parameters.get("oauth_version"))) + { + message = "OAuth version not supported" + httpCode = 400 + } + //supported signature method + else if (! supportedSignatureMethod(parameters.get("oauth_signature_method").get)) + { + message = "Unsupported signature method, please use hmac-sha128 or hmac-sha256" + httpCode = 400 + } + //check if the application is registered and active + else if(! registeredApplication(parameters.get("oauth_consumer_key").get)) + { + logger.error("application: " + parameters.get("oauth_consumer_key").get + " not found") + message = "Invalid consumer credentials" + httpCode = 401 + } + //valid timestamp + else if(wrongTimestamp(new Date(parameters.get("oauth_timestamp").get.toLong))) + { + message = "wrong timestamps" + httpCode = 400 + } + //unused nonce + else if (alReadyUsedNonce(parameters)) + { + message = "Nonce already used" + httpCode = 401 + } + //In the case OAuth authorization token request, check if the token is still valid and the verifier is correct + else if(requestType=="authorizationToken" && !validToken(parameters.get("oauth_token").get, parameters.get("oauth_verifier").get)) + { + message = "Invalid or expired request token: " + parameters.get("oauth_token").get + httpCode = 401 + } + //In the case protected resource access request, check if the token is still valid + else if ( + requestType=="protectedResource" && + ! validToken2(parameters.get("oauth_token").get) + ) + { + message = "Invalid or expired access token: " + parameters.get("oauth_token").get + httpCode = 401 + } + //checking if the signature is correct + else if(! correctSignature(parameters, httpMethod)) + { + message = "Invalid signature" + httpCode = 401 + } + else + httpCode = 200 + logger.error("error message : " + message) + + (httpCode, message, parameters) + } + private def generateTokenAndSecret() = + { + // generate some random strings + val token_message = Helpers.randomString(40) + val secret_message = Helpers.randomString(40) + + (token_message, secret_message) + } + private def saveRequestToken(oAuthParameters : Map[String, String], tokenKey : String, tokenSecret : String) = + { + import code.model.{Nonce, Token, TokenType} + + val nonce = Nonce.create + nonce.consumerkey(oAuthParameters.get("oauth_consumer_key").get) + nonce.timestamp(new Date(oAuthParameters.get("oauth_timestamp").get.toLong)) + nonce.value(oAuthParameters.get("oauth_nonce").get) + val nonceSaved = nonce.save() + + val token = Token.create + token.tokenType(TokenType.Request) + Consumer.find(By(Consumer.key,oAuthParameters.get("oauth_consumer_key").get)) match { + case Full(consumer) => token.consumerId(consumer.id) + case _ => None + } + token.key(tokenKey) + token.secret(tokenSecret) + if(! oAuthParameters.get("oauth_callback").get.isEmpty) + token.callbackURL(URLDecoder.decode(oAuthParameters.get("oauth_callback").get,"UTF-8")) + else + token.callbackURL("oob") + val currentTime = Platform.currentTime + val tokenDuration : Long = Helpers.minutes(30) + token.duration(tokenDuration) + token.expirationDate(new Date(currentTime+tokenDuration)) + token.insertDate(new Date(currentTime)) + val tokenSaved = token.save() + + nonceSaved && tokenSaved + } + private def saveAuthorizationToken(oAuthParameters : Map[String, String], tokenKey : String, tokenSecret : String) = + { + import code.model.{Nonce, Token, TokenType} + + val nonce = Nonce.create + nonce.consumerkey(oAuthParameters.get("oauth_consumer_key").get) + nonce.timestamp(new Date(oAuthParameters.get("oauth_timestamp").get.toLong)) + nonce.tokenKey(oAuthParameters.get("oauth_token").get) + nonce.value(oAuthParameters.get("oauth_nonce").get) + val nonceSaved = nonce.save() + + val token = Token.create + token.tokenType(TokenType.Access) + Consumer.find(By(Consumer.key,oAuthParameters.get("oauth_consumer_key").get)) match { + case Full(consumer) => token.consumerId(consumer.id) + case _ => None + } + Token.find(By(Token.key, oAuthParameters.get("oauth_token").get)) match { + case Full(requestToken) => token.userId(requestToken.userId) + case _ => None + } + token.key(tokenKey) + token.secret(tokenSecret) + val currentTime = Platform.currentTime + val tokenDuration : Long = Helpers.weeks(4) + token.duration(tokenDuration) + token.expirationDate(new Date(currentTime+tokenDuration)) + token.insertDate(new Date(currentTime)) + val tokenSaved = token.save() + + nonceSaved && tokenSaved + } + + def getUser : Box[User] = { + val httpMethod = S.request match { + case Full(r) => r.request.method + case _ => "GET" + } + val (httpCode, message, oAuthParameters) = validator("protectedResource", httpMethod) + + //TODO: Needs refactoring + if(httpCode== 200) getUser(httpCode, oAuthParameters.get("oauth_token")) + else Failure(message) + } + + def getUser(httpCode : Int, tokenID : Box[String]) : Box[User] = + if(httpCode==200) + { + import code.model.Token + logger.info("OAuth header correct ") + Token.find(By(Token.key, tokenID.get)) match { + case Full(token) => { + logger.info("access token: "+ token + " found") + val user = User.findById(token.userId.get) + //just a log + user match { + case Full(u) => logger.info("user " + u.emailAddress + " was found from the oauth token") + case _ => logger.info("no user was found for the oauth token") + } + user + } + case _ =>{ + logger.warn("no token " + tokenID.get + " found") + Empty + } + } + } + else + Empty +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/api/v1_2/JSONFactory.scala b/MavLift/src/main/scala/code/api/v1_2/JSONFactory.scala new file mode 100644 index 000000000..e16168b5e --- /dev/null +++ b/MavLift/src/main/scala/code/api/v1_2/JSONFactory.scala @@ -0,0 +1,546 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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_2 + +import java.util.Date +import net.liftweb.common.{Box, Full} +import code.model._ + +case class APIInfoJSON( + version : String, + git_commit : String, + hosted_by : HostedBy +) +case class HostedBy( + organisation : String, + email : String, + phone : String +) +case class ErrorMessage( + error : String +) +case class SuccessMessage( + success : String +) +case class BanksJSON( + banks : List[BankJSON] +) +case class MinimalBankJSON( + national_identifier : String, + name : String +) +case class BankJSON( + id : String, + short_name : String, + full_name : String, + logo : String, + website : String +) +case class ViewsJSON( + views : List[ViewJSON] +) +case class ViewJSON( + id : String, + short_name : String, + description : String, + is_public : Boolean +) +case class AccountsJSON( + accounts : List[AccountJSON] +) +case class AccountJSON( + id : String, + label : String, + views_available : Set[ViewJSON], + bank_id : String +) +case class ModeratedAccountJSON( + id : String, + label : String, + number : String, + owners : List[UserJSON], + `type` : String, + balance : AmountOfMoneyJSON, + IBAN : String, + views_available : Set[ViewJSON], + bank_id : String +) +case class UserJSON( + id : String, + provider : String, + display_name : String +) +case class PermissionsJSON( + permissions : List[PermissionJSON] +) +case class PermissionJSON( + user : UserJSON, + views : List[ViewJSON] +) +case class AmountOfMoneyJSON( + currency : String, + amount : String +) +case class AccountHolderJSON( + name : String, + is_alias : Boolean +) +case class ThisAccountJSON( + id : String, + holders : List[AccountHolderJSON], + number : String, + kind : String, + IBAN : String, + bank : MinimalBankJSON +) +case class OtherAccountsJSON( + other_accounts : List[OtherAccountJSON] +) +case class OtherAccountJSON( + id : String, + holder : AccountHolderJSON, + number : String, + kind : String, + IBAN : String, + bank : MinimalBankJSON, + metadata : OtherAccountMetadataJSON +) +case class OtherAccountMetadataJSON( + public_alias : String, + private_alias : String, + more_info : String, + URL : String, + image_URL : String, + open_corporates_URL : String, + corporate_location : LocationJSON, + physical_location : LocationJSON +) +case class LocationJSON( + latitude : Double, + longitude : Double, + date : Date, + user : UserJSON +) +case class TransactionDetailsJSON( + `type` : String, + label : String, + posted : Date, + completed : Date, + new_balance : AmountOfMoneyJSON, + value : AmountOfMoneyJSON +) +case class TransactionMetadataJSON( + narrative : String, + comments : List[TransactionCommentJSON], + tags : List[TransactionTagJSON], + images : List[TransactionImageJSON], + where : LocationJSON +) +case class TransactionsJSON( + transactions: List[TransactionJSON] +) +case class TransactionJSON( + id : String, + this_account : ThisAccountJSON, + other_account : OtherAccountJSON, + details : TransactionDetailsJSON, + metadata : TransactionMetadataJSON +) +case class TransactionImagesJSON( + images : List[TransactionImageJSON] +) +case class TransactionImageJSON( + id : String, + label : String, + URL : String, + date : Date, + user : UserJSON +) +case class PostTransactionImageJSON( + label : String, + URL : String +) +case class PostTransactionCommentJSON( + value: String +) +case class PostTransactionTagJSON( + value : String +) +case class TransactionTagJSON( + id : String, + value : String, + date : Date, + user : UserJSON +) +case class TransactionTagsJSON( + tags: List[TransactionTagJSON] +) +case class TransactionCommentJSON( + id : String, + value : String, + date: Date, + user : UserJSON +) +case class TransactionCommentsJSON( + comments: List[TransactionCommentJSON] +) +case class TransactionWhereJSON( + where: LocationJSON +) +case class PostTransactionWhereJSON( + where: LocationPlainJSON +) +case class AliasJSON( + alias: String +) +case class MoreInfoJSON( + more_info: String +) +case class UrlJSON( + URL:String +) +case class ImageUrlJSON( + image_URL: String +) +case class OpenCorporateUrlJSON( + open_corporates_URL: String +) +case class CorporateLocationJSON( + corporate_location: LocationPlainJSON +) +case class PhysicalLocationJSON( + physical_location: LocationPlainJSON +) +case class LocationPlainJSON( + latitude : Double, + longitude : Double +) +case class TransactionNarrativeJSON( + narrative : String +) + +case class ViewIdsJson( + views : List[String] +) + +object JSONFactory{ + def stringOrNull(text : String) = + if(text.isEmpty) + null + else + text + + def stringOptionOrNull(text : Option[String]) = + text match { + case Some(t) => stringOrNull(t) + case _ => null + } + + def createBankJSON(bank : Bank) : BankJSON = { + new BankJSON( + stringOrNull(bank.permalink), + stringOrNull(bank.shortName), + stringOrNull(bank.fullName), + stringOrNull(bank.logoURL), + stringOrNull(bank.website) + ) + } + + def createViewsJSON(views : List[View]) : ViewsJSON = { + val list : List[ViewJSON] = views.map(createViewJSON) + new ViewsJSON(list) + } + + def createViewJSON(view : View) : ViewJSON = { + new ViewJSON( + view.permalink, + stringOrNull(view.name), + stringOrNull(view.description), + view.isPublic + ) + } + + def createAccountJSON(account : BankAccount, viewsAvailable : Set[ViewJSON] ) : AccountJSON = { + new AccountJSON( + account.permalink, + stringOrNull(account.label), + viewsAvailable, + account.bankPermalink + ) + } + + def createBankAccountJSON(account : ModeratedBankAccount, viewsAvailable : Set[ViewJSON]) : ModeratedAccountJSON = { + val bankName = account.bankName.getOrElse("") + new ModeratedAccountJSON( + account.id, + stringOptionOrNull(account.label), + stringOptionOrNull(account.number), + createOwnersJSON(account.owners.getOrElse(Set()), bankName), + stringOptionOrNull(account.accountType), + createAmountOfMoneyJSON(account.currency.getOrElse(""), account.balance), + stringOptionOrNull(account.iban), + viewsAvailable, + stringOptionOrNull(account.bankPermalink) + ) + } + + def createTransactionsJSON(transactions: List[ModeratedTransaction]) : TransactionsJSON = { + new TransactionsJSON(transactions.map(createTransactionJSON)) + } + + def createTransactionJSON(transaction : ModeratedTransaction) : TransactionJSON = { + new TransactionJSON( + id = transaction.id, + this_account = transaction.bankAccount.map(createThisAccountJSON).getOrElse(null), + other_account = transaction.otherBankAccount.map(createOtherBankAccount).getOrElse(null), + details = createTransactionDetailsJSON(transaction), + metadata = transaction.metadata.map(createTransactionMetadataJSON).getOrElse(null) + ) + } + + def createTransactionCommentsJSON(comments : List[Comment]) : TransactionCommentsJSON = { + new TransactionCommentsJSON(comments.map(createTransactionCommentJSON)) + } + + def createTransactionCommentJSON(comment : Comment) : TransactionCommentJSON = { + new TransactionCommentJSON( + id = comment.id_, + value = comment.text, + date = comment.datePosted, + user = createUserJSON(comment.postedBy) + ) + } + + def createTransactionImagesJSON(images : List[TransactionImage]) : TransactionImagesJSON = { + new TransactionImagesJSON(images.map(createTransactionImageJSON)) + } + + def createTransactionImageJSON(image : TransactionImage) : TransactionImageJSON = { + new TransactionImageJSON( + id = image.id_, + label = image.description, + URL = image.imageUrl.toString, + date = image.datePosted, + user = createUserJSON(image.postedBy) + ) + } + + def createTransactionTagsJSON(tags : List[Tag]) : TransactionTagsJSON = { + new TransactionTagsJSON(tags.map(createTransactionTagJSON)) + } + + def createTransactionTagJSON(tag : Tag) : TransactionTagJSON = { + new TransactionTagJSON( + id = tag.id_, + value = tag.value, + date = tag.datePosted, + user = createUserJSON(tag.postedBy) + ) + } + + def createLocationJSON(location : GeoTag) : LocationJSON = { + val user = createUserJSON(location.postedBy) + //test if the GeoTag is set to its default value + if(location.latitude == 0.0 & location.longitude == 0.0 & user == null) + null + else + new LocationJSON( + latitude = location.latitude, + longitude = location.longitude, + date = location.datePosted, + user = user + ) + } + + def createLocationPlainJSON(lat: Double, lon: Double) : LocationPlainJSON = { + new LocationPlainJSON( + latitude = lat, + longitude = lon + ) + } + + def createTransactionMetadataJSON(metadata : ModeratedTransactionMetadata) : TransactionMetadataJSON = { + new TransactionMetadataJSON( + narrative = stringOptionOrNull(metadata.ownerComment), + comments = metadata.comments.map(_.map(createTransactionCommentJSON)).getOrElse(null), + tags = metadata.tags.map(_.map(createTransactionTagJSON)).getOrElse(null), + images = metadata.images.map(_.map(createTransactionImageJSON)).getOrElse(null), + where = metadata.whereTag.map(createLocationJSON).getOrElse(null) + ) + } + + def createTransactionDetailsJSON(transaction : ModeratedTransaction) : TransactionDetailsJSON = { + new TransactionDetailsJSON( + `type` = stringOptionOrNull(transaction.transactionType), + label = stringOptionOrNull(transaction.label), + posted = transaction.startDate.getOrElse(null), + completed = transaction.finishDate.getOrElse(null), + new_balance = createAmountOfMoneyJSON(transaction.currency, transaction.balance), + value= createAmountOfMoneyJSON(transaction.currency, transaction.amount.map(_.toString)) + ) + } + + def createMinimalBankJSON(bankAccount : ModeratedBankAccount) : MinimalBankJSON = { + new MinimalBankJSON( + national_identifier = stringOptionOrNull(bankAccount.nationalIdentifier), + name = stringOptionOrNull(bankAccount.bankName) + ) + } + + def createMinimalBankJSON(bankAccount : ModeratedOtherBankAccount) : MinimalBankJSON = { + new MinimalBankJSON( + national_identifier = stringOptionOrNull(bankAccount.nationalIdentifier), + name = stringOptionOrNull(bankAccount.bankName) + ) + } + + def createThisAccountJSON(bankAccount : ModeratedBankAccount) : ThisAccountJSON = { + new ThisAccountJSON( + id = bankAccount.id, + number = stringOptionOrNull(bankAccount.number), + kind = stringOptionOrNull(bankAccount.accountType), + IBAN = stringOptionOrNull(bankAccount.iban), + bank = createMinimalBankJSON(bankAccount), + holders = null //TODO //bankAccount.owners.map(x => x.toList.map(h => createAccountHolderJSON(h, ??))).getOrElse(null) + ) + } + + def createAccountHolderJSON(owner : AccountOwner, isAlias : Boolean) : AccountHolderJSON = { + new AccountHolderJSON( + name = owner.name, + is_alias = isAlias + ) + } + + def createAccountHolderJSON(name : String, isAlias : Boolean) : AccountHolderJSON = { + new AccountHolderJSON( + name = name, + is_alias = isAlias + ) + } + + def createOtherAccountMetaDataJSON(metadata : ModeratedOtherBankAccountMetadata) : OtherAccountMetadataJSON = { + new OtherAccountMetadataJSON( + public_alias = stringOptionOrNull(metadata.publicAlias), + private_alias = stringOptionOrNull(metadata.privateAlias), + more_info = stringOptionOrNull(metadata.moreInfo), + URL = stringOptionOrNull(metadata.url), + image_URL = stringOptionOrNull(metadata.imageURL), + open_corporates_URL = stringOptionOrNull(metadata.openCorporatesURL), + corporate_location = metadata.corporateLocation.map(createLocationJSON).getOrElse(null), + physical_location = metadata.physicalLocation.map(createLocationJSON).getOrElse(null) + ) + } + + def createOtherBankAccount(bankAccount : ModeratedOtherBankAccount) : OtherAccountJSON = { + new OtherAccountJSON( + id = bankAccount.id, + number = stringOptionOrNull(bankAccount.number), + kind = stringOptionOrNull(bankAccount.kind), + IBAN = stringOptionOrNull(bankAccount.iban), + bank = createMinimalBankJSON(bankAccount), + holder = createAccountHolderJSON(bankAccount.label.display, bankAccount.isAlias), + metadata = bankAccount.metadata.map(createOtherAccountMetaDataJSON).getOrElse(null) + ) + } + + def createOtherBankAccountsJSON(otherBankAccounts : List[ModeratedOtherBankAccount]) : OtherAccountsJSON = { + val otherAccountsJSON : List[OtherAccountJSON] = otherBankAccounts.map(createOtherBankAccount) + OtherAccountsJSON(otherAccountsJSON) + } + + def createUserJSON(user : User) : UserJSON = { + new UserJSON( + user.id_, + stringOrNull(user.provider), + stringOrNull(user.emailAddress) + ) + } + + def createUserJSON(user : Box[User]) : UserJSON = { + user match { + case Full(u) => createUserJSON(u) + case _ => null + } + } + + def createOwnersJSON(owners : Set[AccountOwner], bankName : String) : List[UserJSON] = { + owners.map(o => { + new UserJSON( + o.id, + stringOrNull(bankName), + stringOrNull(o.name) + ) + } + ).toList + } + + def createAmountOfMoneyJSON(currency : String, amount : String) : AmountOfMoneyJSON = { + new AmountOfMoneyJSON( + stringOrNull(currency), + stringOrNull(amount) + ) + } + + def createAmountOfMoneyJSON(currency : Option[String], amount : Option[String]) : AmountOfMoneyJSON = { + new AmountOfMoneyJSON( + stringOptionOrNull(currency), + stringOptionOrNull(amount) + ) + } + + def createAmountOfMoneyJSON(currency : Option[String], amount : String) : AmountOfMoneyJSON = { + new AmountOfMoneyJSON( + stringOptionOrNull(currency), + stringOrNull(amount) + ) + } + + def createPermissionsJSON(permissions : List[Permission]) : PermissionsJSON = { + val permissionsJson = permissions.map(p => { + new PermissionJSON( + createUserJSON(p.user), + p.views.map(createViewJSON) + ) + }) + new PermissionsJSON(permissionsJson) + } + + def createAliasJSON(alias: String): AliasJSON = { + AliasJSON(stringOrNull(alias)) + } + + def createTransactionNarrativeJSON(narrative: String): TransactionNarrativeJSON = { + TransactionNarrativeJSON(stringOrNull(narrative)) + } + +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/api/v1_2/OBPAPI1.2.scala b/MavLift/src/main/scala/code/api/v1_2/OBPAPI1.2.scala new file mode 100644 index 000000000..2b2374b16 --- /dev/null +++ b/MavLift/src/main/scala/code/api/v1_2/OBPAPI1.2.scala @@ -0,0 +1,1175 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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_2 + +import net.liftweb.http.JsonResponse +import net.liftweb.http.rest._ +import net.liftweb.json.JsonDSL._ +import net.liftweb.json.Printer._ +import net.liftweb.json.Extraction +import net.liftweb.json.JsonAST._ +import net.liftweb.common.{Failure,Full,Empty, Box, Loggable} +import net.liftweb.mongodb._ +import com.mongodb.casbah.Imports._ +import _root_.java.math.MathContext +import org.bson.types._ +import _root_.net.liftweb.util._ +import _root_.net.liftweb.mapper._ +import _root_.net.liftweb.util.Helpers._ +import _root_.scala.xml._ +import _root_.net.liftweb.http.S._ +import net.liftweb.mongodb.{ Skip, Limit } +import _root_.net.liftweb.mapper.view._ +import com.mongodb._ +import java.util.Date +import code.api.OAuthHandshake._ +import code.model.dataAccess.OBPEnvelope.{OBPOrder, OBPLimit, OBPOffset, OBPOrdering, OBPFromDate, OBPToDate, OBPQueryParam} +import code.model._ +import java.net.URL +import code.util.APIUtil._ +import code.api.OBPRestHelper + + +object OBPAPI1_2 extends OBPRestHelper with Loggable { + + implicit def errorToJson(error: ErrorMessage): JValue = Extraction.decompose(error) + implicit def successToJson(success: SuccessMessage): JValue = Extraction.decompose(success) + + val dateFormat = ModeratedTransaction.dateFormat + val apiPrefix = "obp" / "v1.2" oPrefix _ + + private def bankAccountsListToJson(bankAccounts: List[BankAccount], user : Box[User]): JValue = { + val accJson : List[AccountJSON] = bankAccounts.map( account => { + val views = account permittedViews user + val viewsAvailable : Set[ViewJSON] = + views.map( v => { + JSONFactory.createViewJSON(v) + }) + JSONFactory.createAccountJSON(account,viewsAvailable) + }) + + val accounts = new AccountsJSON(accJson) + Extraction.decompose(accounts) + } + + private def moderatedTransactionMetadata(bankId : String, accountId : String, viewId : String, transactionID : String, user : Box[User]) : Box[ModeratedTransactionMetadata] = + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + moderatedTransaction <- account.moderatedTransaction(transactionID, view, user) + metadata <- Box(moderatedTransaction.metadata) ?~ {"view " + viewId + " does not authorize metadata access"} + } yield metadata + + oauthServe(apiPrefix { + case Nil JsonGet json => { + user => + val apiDetails: JValue = { + val hostedBy = new HostedBy("TESOBE", "contact@tesobe.com", "+49 (0)30 8145 3994") + val apiInfoJSON = new APIInfoJSON("1.2", gitCommit, hostedBy) + Extraction.decompose(apiInfoJSON) + } + + Full(successJsonResponse(apiDetails, 200)) + } + }) + + oauthServe(apiPrefix { + //get banks + case "banks" :: Nil JsonGet json => { + user => + def banksToJson(banksList: List[Bank]): JValue = { + val banksJSON: List[BankJSON] = banksList.map(b => { + JSONFactory.createBankJSON(b) + }) + val banks = new BanksJSON(banksJSON) + Extraction.decompose(banks) + } + + Full(successJsonResponse(banksToJson(Bank.all))) + } + }) + + oauthServe(apiPrefix{ + //get bank by id + case "banks" :: bankId :: Nil JsonGet json => { + user => + def bankToJson(bank : Bank) : JValue = { + val bankJSON = JSONFactory.createBankJSON(bank) + Extraction.decompose(bankJSON) + } + for(bank <- Bank(bankId)) + yield successJsonResponse(bankToJson(bank)) + } + }) + + oauthServe(apiPrefix { + //get accounts + case "banks" :: bankId :: "accounts" :: Nil JsonGet json => { + user => + for{ + bank <- Bank(bankId) + availableAccounts <- bank.accounts(user) + } yield successJsonResponse(bankAccountsListToJson(availableAccounts, user)) + } + }) + + oauthServe(apiPrefix { + //get private accounts + case "banks" :: bankId :: "accounts" :: "private" :: Nil JsonGet json => { + user => + for { + u <- user ?~ "user not found" + bank <- Bank(bankId) + availableAccounts <- bank.nonPublicAccounts(u) + } yield successJsonResponse(bankAccountsListToJson(availableAccounts, Full(u))) + } + }) + + oauthServe(apiPrefix { + //get public accounts + case "banks" :: bankId :: "accounts" :: "public" :: Nil JsonGet json => { + user => + for { + bank <- Bank(bankId) + availableAccounts <- bank.publicAccounts + } yield { + val publicAccountsJson = bankAccountsListToJson(availableAccounts, user) + successJsonResponse(publicAccountsJson) + } + } + }) + + oauthServe(apiPrefix { + //get account by id + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "account" :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + availableviews <- Full(account.permittedViews(user)) + view <- View.fromUrl(viewId) + moderatedAccount <- account.moderatedBankAccount(view, user) + } yield { + val viewsAvailable = availableviews.map(JSONFactory.createViewJSON) + val moderatedAccountJson = JSONFactory.createBankAccountJSON(moderatedAccount, viewsAvailable) + successJsonResponse(Extraction.decompose(moderatedAccountJson)) + } + } + }) + + oauthServe(apiPrefix { + //get the available views on an bank account + case "banks" :: bankId :: "accounts" :: accountId :: "views" :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + u <- user ?~ "user not found" + views <- account views u + } yield { + val viewsJSON = JSONFactory.createViewsJSON(views) + successJsonResponse(Extraction.decompose(viewsJSON)) + } + } + }) + + oauthServe(apiPrefix { + //get access + case "banks" :: bankId :: "accounts" :: accountId :: "permissions" :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + u <- user ?~ "user not found" + permissions <- account permissions u + } yield { + val permissionsJSON = JSONFactory.createPermissionsJSON(permissions) + successJsonResponse(Extraction.decompose(permissionsJSON)) + } + } + }) + + oauthServe(apiPrefix { + //get access for specific user + case "banks" :: bankId :: "accounts" :: accountId :: "permissions" :: userId :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + u <- user ?~ "user not found" + permissions <- account permissions u + userPermission <- Box(permissions.find(p => { p.user.id_ == userId})) ?~ {"None permission found for user "+userId} + } yield { + val views = JSONFactory.createViewsJSON(userPermission.views) + successJsonResponse(Extraction.decompose(views)) + } + } + }) + + oauthServe(apiPrefix{ + //add access for specific user to a list of views + case "banks" :: bankId :: "accounts" :: accountId :: "permissions" :: userId :: "views" :: Nil JsonPost json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + u <- user ?~ "user not found" + viewIds <- tryo{json.extract[ViewIdsJson]} ?~ "wrong format JSON" + addedViews <- account addPermissions(u, viewIds.views, userId) + } yield { + val viewJson = JSONFactory.createViewsJSON(addedViews) + successJsonResponse(Extraction.decompose(viewJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //add access for specific user to a specific view + case "banks" :: bankId :: "accounts" :: accountId :: "permissions" :: userId :: "views" :: viewId :: Nil JsonPost json => { + user => + for { + account <- BankAccount(bankId, accountId) + u <- user ?~ "user not found" + view <- View.fromUrl(viewId) + isAdded <- account addPermission(u, viewId, userId) + if(isAdded) + } yield { + val viewJson = JSONFactory.createViewJSON(view) + successJsonResponse(Extraction.decompose(viewJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //delete access for specific user to one view + case "banks" :: bankId :: "accounts" :: accountId :: "permissions" :: userId :: "views" :: viewId :: Nil JsonDelete json => { + user => + for { + account <- BankAccount(bankId, accountId) + u <- user ?~ "user not found" + isRevoked <- account revokePermission(u, viewId, userId) + if(isRevoked) + } yield noContentJsonResponse + } + }) + + oauthServe(apiPrefix{ + //delete access for specific user to all the views + case "banks" :: bankId :: "accounts" :: accountId :: "permissions" :: userId :: "views" :: Nil JsonDelete json => { + user => + for { + account <- BankAccount(bankId, accountId) + u <- user ?~ "user not found" + isRevoked <- account revokeAllPermission(u, userId) + if(isRevoked) + } yield noContentJsonResponse + } + }) + + oauthServe(apiPrefix{ + //get other accounts for one account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccounts <- account.moderatedOtherBankAccounts(view, user) + } yield { + val otherBankAccountsJson = JSONFactory.createOtherBankAccountsJSON(otherBankAccounts) + successJsonResponse(Extraction.decompose(otherBankAccountsJson)) + } + } + }) + + oauthServe(apiPrefix{ + //get one other account by id + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + } yield { + val otherBankAccountJson = JSONFactory.createOtherBankAccount(otherBankAccount) + successJsonResponse(Extraction.decompose(otherBankAccountJson)) + } + } + }) + + oauthServe(apiPrefix{ + //get metadata of one other account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "metadata" :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + } yield { + val metadataJson = JSONFactory.createOtherAccountMetaDataJSON(metadata) + successJsonResponse(Extraction.decompose(metadataJson)) + } + } + }) + + oauthServe(apiPrefix{ + //get public alias of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "public_alias" :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + alias <- Box(metadata.publicAlias) ?~ {"the view " + viewId + "does not allow public alias access"} + } yield { + val aliasJson = JSONFactory.createAliasJSON(alias) + successJsonResponse(Extraction.decompose(aliasJson)) + } + } + }) + + oauthServe(apiPrefix{ + //add public alias to other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "public_alias" :: Nil JsonPost json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addAlias <- Box(metadata.addPublicAlias) ?~ {"the view " + viewId + "does not allow adding a public alias"} + aliasJson <- tryo{(json.extract[AliasJSON])} ?~ {"wrong JSON format"} + if(addAlias(aliasJson.alias)) + } yield { + successJsonResponse(Extraction.decompose(SuccessMessage("public alias added")), 201) + } + } + }) + + oauthServe(apiPrefix{ + //update public alias of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "public_alias" :: Nil JsonPut json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addAlias <- Box(metadata.addPublicAlias) ?~ {"the view " + viewId + "does not allow updating the public alias"} + aliasJson <- tryo{(json.extract[AliasJSON])} ?~ {"wrong JSON format"} + if(addAlias(aliasJson.alias)) + } yield { + successJsonResponse(Extraction.decompose(SuccessMessage("public alias updated"))) + } + } + }) + + oauthServe(apiPrefix{ + //delete public alias of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "public_alias" :: Nil JsonDelete _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addAlias <- Box(metadata.addPublicAlias) ?~ {"the view " + viewId + "does not allow deleting the public alias"} + if(addAlias("")) + } yield noContentJsonResponse + } + }) + + + oauthServe(apiPrefix{ + //get private alias of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "private_alias" :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + alias <- Box(metadata.privateAlias) ?~ {"the view " + viewId + "does not allow private alias access"} + } yield { + val aliasJson = JSONFactory.createAliasJSON(alias) + successJsonResponse(Extraction.decompose(aliasJson)) + } + } + }) + + oauthServe(apiPrefix{ + //add private alias to other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "private_alias" :: Nil JsonPost json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addAlias <- Box(metadata.addPrivateAlias) ?~ {"the view " + viewId + "does not allow adding a private alias"} + aliasJson <- tryo{(json.extract[AliasJSON])} ?~ {"wrong JSON format"} + if(addAlias(aliasJson.alias)) + } yield { + val successJson = SuccessMessage("private alias added") + successJsonResponse(Extraction.decompose(successJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //update private alias of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "private_alias" :: Nil JsonPut json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addAlias <- Box(metadata.addPrivateAlias) ?~ {"the view " + viewId + "does not allow updating the private alias"} + aliasJson <- tryo{(json.extract[AliasJSON])} ?~ {"wrong JSON format"} + if(addAlias(aliasJson.alias)) + } yield { + val successJson = SuccessMessage("private alias updated") + successJsonResponse(Extraction.decompose(successJson)) + } + } + }) + + oauthServe(apiPrefix{ + //delete private alias of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "private_alias" :: Nil JsonDelete _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addAlias <- Box(metadata.addPrivateAlias) ?~ {"the view " + viewId + "does not allow deleting the private alias"} + if(addAlias("")) + } yield noContentJsonResponse + } + }) + + oauthServe(apiPrefix{ + //add more info to other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "more_info" :: Nil JsonPost json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addMoreInfo <- Box(metadata.addMoreInfo) ?~ {"the view " + viewId + "does not allow adding more info"} + moreInfoJson <- tryo{(json.extract[MoreInfoJSON])} ?~ {"wrong JSON format"} + if(addMoreInfo(moreInfoJson.more_info)) + } yield { + val successJson = SuccessMessage("more info added") + successJsonResponse(Extraction.decompose(successJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //update more info of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "more_info" :: Nil JsonPut json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addMoreInfo <- Box(metadata.addMoreInfo) ?~ {"the view " + viewId + "does not allow updating more info"} + moreInfoJson <- tryo{(json.extract[MoreInfoJSON])} ?~ {"wrong JSON format"} + if(addMoreInfo(moreInfoJson.more_info)) + } yield { + val successJson = SuccessMessage("more info updated") + successJsonResponse(Extraction.decompose(successJson)) + } + } + }) + + oauthServe(apiPrefix{ + //delete more info of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "more_info" :: Nil JsonDelete _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addMoreInfo <- Box(metadata.addMoreInfo) ?~ {"the view " + viewId + "does not allow deleting more info"} + if(addMoreInfo("")) + } yield noContentJsonResponse + } + }) + + oauthServe(apiPrefix{ + //add url to other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "url" :: Nil JsonPost json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addUrl <- Box(metadata.addURL) ?~ {"the view " + viewId + "does not allow adding a url"} + urlJson <- tryo{(json.extract[UrlJSON])} ?~ {"wrong JSON format"} + if(addUrl(urlJson.URL)) + } yield { + val successJson = SuccessMessage("url added") + successJsonResponse(Extraction.decompose(successJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //update url of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "url" :: Nil JsonPut json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addUrl <- Box(metadata.addURL) ?~ {"the view " + viewId + "does not allow updating a url"} + urlJson <- tryo{(json.extract[UrlJSON])} ?~ {"wrong JSON format"} + if(addUrl(urlJson.URL)) + } yield { + val successJson = SuccessMessage("url updated") + successJsonResponse(Extraction.decompose(successJson)) + } + } + }) + + oauthServe(apiPrefix{ + //delete url of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "url" :: Nil JsonDelete _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addUrl <- Box(metadata.addURL) ?~ {"the view " + viewId + "does not allow deleting a url"} + if(addUrl("")) + } yield noContentJsonResponse + } + }) + + oauthServe(apiPrefix{ + //add image url to other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "image_url" :: Nil JsonPost json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addImageUrl <- Box(metadata.addImageURL) ?~ {"the view " + viewId + "does not allow adding an image url"} + imageUrlJson <- tryo{(json.extract[ImageUrlJSON])} ?~ {"wrong JSON format"} + if(addImageUrl(imageUrlJson.image_URL)) + } yield { + val successJson = SuccessMessage("image url added") + successJsonResponse(Extraction.decompose(successJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //update image url of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "image_url" :: Nil JsonPut json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addImageUrl <- Box(metadata.addImageURL) ?~ {"the view " + viewId + "does not allow updating an image url"} + imageUrlJson <- tryo{(json.extract[ImageUrlJSON])} ?~ {"wrong JSON format"} + if(addImageUrl(imageUrlJson.image_URL)) + } yield { + val successJson = SuccessMessage("image url updated") + successJsonResponse(Extraction.decompose(successJson)) + } + } + }) + + oauthServe(apiPrefix{ + //delete image url of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "image_url" :: Nil JsonDelete _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addImageUrl <- Box(metadata.addImageURL) ?~ {"the view " + viewId + "does not allow deleting an image url"} + if(addImageUrl("")) + } yield noContentJsonResponse + } + }) + + oauthServe(apiPrefix{ + //add open corporate url to other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "open_corporates_url" :: Nil JsonPost json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addOpenCorpUrl <- Box(metadata.addOpenCorporatesURL) ?~ {"the view " + viewId + "does not allow adding an open corporate url"} + opernCoprUrl <- tryo{(json.extract[OpenCorporateUrlJSON])} ?~ {"wrong JSON format"} + if(addOpenCorpUrl(opernCoprUrl.open_corporates_URL)) + } yield { + val successJson = SuccessMessage("open corporate url added") + successJsonResponse(Extraction.decompose(successJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //update open corporate url of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "open_corporates_url" :: Nil JsonPut json -> _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addOpenCorpUrl <- Box(metadata.addOpenCorporatesURL) ?~ {"the view " + viewId + "does not allow updating an open corporate url"} + opernCoprUrl <- tryo{(json.extract[OpenCorporateUrlJSON])} ?~ {"wrong JSON format"} + if(addOpenCorpUrl(opernCoprUrl.open_corporates_URL)) + } yield { + val successJson = SuccessMessage("open corporate url updated") + successJsonResponse(Extraction.decompose(successJson)) + } + } + }) + + oauthServe(apiPrefix{ + //delete open corporate url of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "open_corporates_url" :: Nil JsonDelete _ => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addOpenCorpUrl <- Box(metadata.addOpenCorporatesURL) ?~ {"the view " + viewId + "does not allow deleting an open corporate url"} + if(addOpenCorpUrl("")) + } yield noContentJsonResponse + } + }) + + oauthServe(apiPrefix{ + //add corporate location to other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: other_account_id :: "corporate_location" :: Nil JsonPost json -> _ => { + user => + for { + u <- user + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addCorpLocation <- Box(metadata.addCorporateLocation) ?~ {"the view " + viewId + "does not allow adding a corporate location"} + corpLocationJson <- tryo{(json.extract[CorporateLocationJSON])} ?~ {"wrong JSON format"} + correctCoordinates <- checkIfLocationPossible(corpLocationJson.corporate_location.latitude, corpLocationJson.corporate_location.longitude) + if(addCorpLocation(u.id_, view.id, (now:TimeSpan), corpLocationJson.corporate_location.longitude, corpLocationJson.corporate_location.latitude)) + } yield { + val successJson = SuccessMessage("corporate location added") + successJsonResponse(Extraction.decompose(successJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //update corporate location of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "corporate_location" :: Nil JsonPut json -> _ => { + user => + for { + u <- user + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addCorpLocation <- Box(metadata.addCorporateLocation) ?~ {"the view " + viewId + "does not allow updating a corporate location"} + corpLocationJson <- tryo{(json.extract[CorporateLocationJSON])} ?~ {"wrong JSON format"} + correctCoordinates <- checkIfLocationPossible(corpLocationJson.corporate_location.latitude, corpLocationJson.corporate_location.longitude) + if(addCorpLocation(u.id_, view.id, now, corpLocationJson.corporate_location.longitude, corpLocationJson.corporate_location.latitude)) + } yield { + val successJson = SuccessMessage("corporate location updated") + successJsonResponse(Extraction.decompose(successJson)) + } + } + }) + + oauthServe(apiPrefix{ + //delete corporate location of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "corporate_location" :: Nil JsonDelete _ => { + user => + for { + u <- user + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + deleted <- Box(metadata.deleteCorporateLocation) + } yield { + if(deleted()) + noContentJsonResponse + else + errorJsonResponse("Delete not completed") + } + } + }) + +def checkIfLocationPossible(lat:Double,lon:Double) : Box[Unit] = { + if(scala.math.abs(lat) <= 90 & scala.math.abs(lon) <= 180) + Full() + else + Failure("Coordinates not possible") +} + + oauthServe(apiPrefix{ + //add physical location to other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts" :: other_account_id :: "physical_location" :: Nil JsonPost json -> _ => { + user => + for { + u <- user + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addPhysicalLocation <- Box(metadata.addPhysicalLocation) ?~ {"the view " + viewId + "does not allow adding a physical location"} + physicalLocationJson <- tryo{(json.extract[PhysicalLocationJSON])} ?~ {"wrong JSON format"} + correctCoordinates <- checkIfLocationPossible(physicalLocationJson.physical_location.latitude, physicalLocationJson.physical_location.longitude) + if(addPhysicalLocation(u.id_, view.id, now, physicalLocationJson.physical_location.longitude, physicalLocationJson.physical_location.latitude)) + } yield { + val successJson = SuccessMessage("physical location added") + successJsonResponse(Extraction.decompose(successJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //update physical location to other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "physical_location" :: Nil JsonPut json -> _ => { + user => + for { + u <- user + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + addPhysicalLocation <- Box(metadata.addPhysicalLocation) ?~ {"the view " + viewId + "does not allow updating a physical location"} + physicalLocationJson <- tryo{(json.extract[PhysicalLocationJSON])} ?~ {"wrong JSON format"} + correctCoordinates <- checkIfLocationPossible(physicalLocationJson.physical_location.latitude, physicalLocationJson.physical_location.longitude) + if(addPhysicalLocation(u.id_, view.id, now, physicalLocationJson.physical_location.longitude, physicalLocationJson.physical_location.latitude)) + } yield { + val successJson = SuccessMessage("physical location updated") + successJsonResponse(Extraction.decompose(successJson)) + } + } + }) + + oauthServe(apiPrefix{ + //delete physical location of other bank account + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "other_accounts":: other_account_id :: "physical_location" :: Nil JsonDelete _ => { + user => + for { + u <- user + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + otherBankAccount <- account.moderatedOtherBankAccount(other_account_id, view, user) + metadata <- Box(otherBankAccount.metadata) ?~ {"the view " + viewId + "does not allow metadata access"} + deleted <- Box(metadata.deletePhysicalLocation) + } yield { + if(deleted()) + noContentJsonResponse + else + errorJsonResponse("Delete not completed") + } + } + }) + + oauthServe(apiPrefix { + //get transactions + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: Nil JsonGet json => { + user => + + 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 + for { + bankAccount <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + transactions <- bankAccount.getModeratedTransactions(user, view, params : _*) + } yield { + val json = JSONFactory.createTransactionsJSON(transactions) + successJsonResponse(Extraction.decompose(json)) + } + } + }) + + oauthServe(apiPrefix { + //get transaction by id + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "transaction" :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + moderatedTransaction <- account.moderatedTransaction(transactionId, view, user) + } yield { + val json = JSONFactory.createTransactionJSON(moderatedTransaction) + successJsonResponse(Extraction.decompose(json)) + } + } + }) + + oauthServe(apiPrefix { + //get narrative + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "narrative" :: Nil JsonGet json => { + user => + for { + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + narrative <- Box(metadata.ownerComment) ?~ { "view " + viewId + " does not authorize narrative access" } + } yield { + val narrativeJson = JSONFactory.createTransactionNarrativeJSON(narrative) + successJsonResponse(Extraction.decompose(narrativeJson)) + } + } + }) + + oauthServe(apiPrefix { + //add narrative + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "narrative" :: Nil JsonPost json -> _ => { + user => + for { + narrativeJson <- tryo{json.extract[TransactionNarrativeJSON]} ?~ {"wrong json format"} + u <- user + view <- View.fromUrl(viewId) + metadata <- moderatedTransactionMetadata(bankId, accountId, view.permalink, transactionId, Full(u)) + addNarrative <- Box(metadata.addOwnerComment) ?~ {"view " + viewId + " does not allow adding a narrative"} + } yield { + addNarrative(narrativeJson.narrative) + val successJson = SuccessMessage("narrative added") + successJsonResponse(Extraction.decompose(successJson), 201) + } + } + }) + + oauthServe(apiPrefix { + //update narrative + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "narrative" :: Nil JsonPut json -> _ => { + user => + for { + narrativeJson <- tryo{json.extract[TransactionNarrativeJSON]} ?~ {"wrong json format"} + u <- user + view <- View.fromUrl(viewId) + metadata <- moderatedTransactionMetadata(bankId, accountId, view.permalink, transactionId, Full(u)) + addNarrative <- Box(metadata.addOwnerComment) ?~ {"view " + viewId + " does not allow updating a narrative"} + } yield { + addNarrative(narrativeJson.narrative) + val successJson = SuccessMessage("narrative updated") + successJsonResponse(Extraction.decompose(successJson)) + } + } + }) + + oauthServe(apiPrefix { + //delete narrative + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "narrative" :: Nil JsonDelete _ => { + user => + for { + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + addNarrative <- Box(metadata.addOwnerComment) ?~ {"view " + viewId + " does not allow deleting the narrative"} + } yield { + addNarrative("") + noContentJsonResponse + } + } + }) + + oauthServe(apiPrefix { + //get comments + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "comments" :: Nil JsonGet json => { + user => + for { + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + comments <- Box(metadata.comments) ?~ { "view " + viewId + " does not authorize comments access" } + } yield { + val json = JSONFactory.createTransactionCommentsJSON(comments) + successJsonResponse(Extraction.decompose(json)) + } + } + }) + + oauthServe(apiPrefix { + //add comment + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "comments" :: Nil JsonPost json -> _ => { + user => + for { + commentJson <- tryo{json.extract[PostTransactionCommentJSON]} ?~ {"wrong json format"} + u <- user + view <- View.fromUrl(viewId) + metadata <- moderatedTransactionMetadata(bankId, accountId, view.permalink, transactionId, Full(u)) + addCommentFunc <- Box(metadata.addComment) ?~ {"view " + viewId + " does not authorize adding comments"} + postedComment <- Full(addCommentFunc(u.id_, view.id, commentJson.value, now)) + } yield { + successJsonResponse(Extraction.decompose(JSONFactory.createTransactionCommentJSON(postedComment)),201) + } + } + }) + + oauthServe(apiPrefix { + //delete comment + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "comments":: commentId :: Nil JsonDelete _ => { + user => + for { + account <- BankAccount(bankId, accountId) + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + delete <- metadata.deleteComment(commentId, user, account) + } yield { + noContentJsonResponse + } + } + }) + + oauthServe(apiPrefix { + //get tags + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "tags" :: Nil JsonGet json => { + user => + for { + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + tags <- Box(metadata.tags) ?~ { "view " + viewId + " does not authorize tag access" } + } yield { + val json = JSONFactory.createTransactionTagsJSON(tags) + successJsonResponse(Extraction.decompose(json)) + } + } + }) + + oauthServe(apiPrefix { + //add a tag + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "tags" :: Nil JsonPost json -> _ => { + + user => + for { + tagJson <- tryo{json.extract[PostTransactionTagJSON]} + u <- user + view <- View.fromUrl(viewId) + metadata <- moderatedTransactionMetadata(bankId, accountId, view.permalink, transactionID, Full(u)) + addTagFunc <- Box(metadata.addTag) ?~ {"view " + viewId + " does not authorize adding tags"} + postedTag <- Full(addTagFunc(u.id_, view.id, tagJson.value, now)) + } yield { + successJsonResponse(Extraction.decompose(JSONFactory.createTransactionTagJSON(postedTag)), 201) + } + } + }) + + oauthServe(apiPrefix { + //delete a tag + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "tags" :: tagId :: Nil JsonDelete _ => { + + user => + for { + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + bankAccount <- BankAccount(bankId, accountId) + deleted <- metadata.deleteTag(tagId, user, bankAccount) + } yield { + noContentJsonResponse + } + } + }) + + oauthServe(apiPrefix { + //get images + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "images" :: Nil JsonGet json => { + user => + for { + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + images <- Box(metadata.images) ?~ { "view " + viewId + " does not authorize images access" } + } yield { + val json = JSONFactory.createTransactionImagesJSON(images) + successJsonResponse(Extraction.decompose(json)) + } + } + }) + + oauthServe(apiPrefix { + //add an image + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionID :: "metadata" :: "images" :: Nil JsonPost json -> _ => { + user => + for { + imageJson <- tryo{json.extract[PostTransactionImageJSON]} + u <- user + view <- View.fromUrl(viewId) + metadata <- moderatedTransactionMetadata(bankId, accountId, view.permalink, transactionID, Full(u)) + addImageFunc <- Box(metadata.addImage) ?~ {"view " + viewId + " does not authorize adding images"} + url <- tryo{new URL(imageJson.URL)} ?~! "Could not parse url string as a valid URL" + postedImage <- Full(addImageFunc(u.id_, view.id, imageJson.label, now, url)) + } yield { + successJsonResponse(Extraction.decompose(JSONFactory.createTransactionImageJSON(postedImage)),201) + } + } + }) + + oauthServe(apiPrefix { + //delete an image + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "images" :: imageId :: Nil JsonDelete _ => { + user => + for { + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + bankAccount <- BankAccount(bankId, accountId) + deleted <- Box(metadata.deleteImage(imageId, user, bankAccount)) + } yield { + noContentJsonResponse + } + } + }) + + oauthServe(apiPrefix { + //get where tag + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "where" :: Nil JsonGet json => { + user => + for { + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + where <- Box(metadata.whereTag) ?~ { "view " + viewId + " does not authorize where tag access" } + } yield { + val json = JSONFactory.createLocationJSON(where) + val whereJson = TransactionWhereJSON(json) + successJsonResponse(Extraction.decompose(whereJson)) + } + } + }) + + oauthServe(apiPrefix{ + //add where tag + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "where" :: Nil JsonPost json -> _ => { + user => + for { + u <- user + view <- View.fromUrl(viewId) + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + addWhereTag <- Box(metadata.addWhereTag) ?~ {"the view " + viewId + "does not allow adding a where tag"} + whereJson <- tryo{(json.extract[PostTransactionWhereJSON])} ?~ {"wrong JSON format"} + correctCoordinates <- checkIfLocationPossible(whereJson.where.latitude, whereJson.where.longitude) + if(addWhereTag(u.id_, view.id, now, whereJson.where.longitude, whereJson.where.latitude)) + } yield { + val successJson = SuccessMessage("where tag added") + successJsonResponse(Extraction.decompose(successJson), 201) + } + } + }) + + oauthServe(apiPrefix{ + //update where tag + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "where" :: Nil JsonPut json -> _ => { + user => + for { + u <- user + view <- View.fromUrl(viewId) + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + addWhereTag <- Box(metadata.addWhereTag) ?~ {"the view " + viewId + "does not allow updating a where tag"} + whereJson <- tryo{(json.extract[PostTransactionWhereJSON])} ?~ {"wrong JSON format"} + correctCoordinates <- checkIfLocationPossible(whereJson.where.latitude, whereJson.where.longitude) + if(addWhereTag(u.id_, view.id, now, whereJson.where.longitude, whereJson.where.latitude)) + } yield { + val successJson = SuccessMessage("where tag updated") + successJsonResponse(Extraction.decompose(successJson)) + } + } + }) + + oauthServe(apiPrefix{ + //delete where tag + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions" :: transactionId :: "metadata" :: "where" :: Nil JsonDelete _ => { + user => + for { + bankAccount <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + metadata <- moderatedTransactionMetadata(bankId, accountId, viewId, transactionId, user) + deleted <- metadata.deleteWhereTag(view.id, user, bankAccount) + } yield { + if(deleted) + noContentJsonResponse + else + errorJsonResponse("Delete not completed") + } + } + }) + + oauthServe(apiPrefix{ + //get other account of a transaction + case "banks" :: bankId :: "accounts" :: accountId :: viewId :: "transactions":: transactionId :: "other_account" :: Nil JsonGet json => { + user => + for { + account <- BankAccount(bankId, accountId) + view <- View.fromUrl(viewId) + transaction <- account.moderatedTransaction(transactionId, view, user) + moderatedOtherBankAccount <- transaction.otherBankAccount + } yield { + val otherBankAccountJson = JSONFactory.createOtherBankAccount(moderatedOtherBankAccount) + successJsonResponse(Extraction.decompose(otherBankAccountJson)) + } + + } + }) +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/BankingData.scala b/MavLift/src/main/scala/code/model/BankingData.scala new file mode 100644 index 000000000..e5d2be384 --- /dev/null +++ b/MavLift/src/main/scala/code/model/BankingData.scala @@ -0,0 +1,365 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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.model + +import scala.math.BigDecimal +import java.util.Date +import scala.collection.immutable.Set +import net.liftweb.json.JsonDSL._ +import net.liftweb.json.JObject +import net.liftweb.json.JsonDSL._ +import net.liftweb.json.JsonAST.JArray +import net.liftweb.common._ +import code.model.dataAccess.{LocalStorage, Account, HostedBank} +import code.model.dataAccess.OBPEnvelope.OBPQueryParam + + +class Bank( + val id: String, + val shortName : String, + val fullName : String, + val permalink : String, + val logoURL : String, + val website : String +) +{ + def accounts(user : Box[User]) : Box[List[BankAccount]] = { + user match { + case Full(u) => nonPublicAccounts(u) + case _ => publicAccounts + } + } + + def publicAccounts : Box[List[BankAccount]] = LocalStorage.getPublicBankAccounts(this) + def nonPublicAccounts(user : User) : Box[List[BankAccount]] = { + LocalStorage.getNonPublicBankAccounts(user, id) + } + + def detailedJson : JObject = { + ("name" -> shortName) ~ + ("website" -> "") ~ + ("email" -> "") + } + + def toJson : JObject = { + ("alias" -> permalink) ~ + ("name" -> shortName) ~ + ("logo" -> "") ~ + ("links" -> linkJson) + } + + def linkJson : JObject = { + ("rel" -> "bank") ~ + ("href" -> {"/" + permalink + "/bank"}) ~ + ("method" -> "GET") ~ + ("title" -> {"Get information about the bank identified by " + permalink}) + } +} + +object Bank { + def apply(bankPermalink: String) : Box[Bank] = LocalStorage.getBank(bankPermalink) + + def all : List[Bank] = LocalStorage.allBanks + + def toJson(banks: Seq[Bank]) : JArray = + banks.map(bank => bank.toJson) + +} + +class AccountOwner( + val id : String, + val name : String +) + +class BankAccount( + val id : String, + val owners : Set[AccountOwner], + val accountType : String, + val balance : BigDecimal, + val currency : String, + val name : String, + val label : String, + val nationalIdentifier : String, + val swift_bic : Option[String], + val iban : Option[String], + val allowAnnoymousAccess : Boolean, + val number : String, + val bankName : String, + val bankPermalink : String, + val permalink : String +) extends Loggable{ + + private def viewNotAllowed(view : View ) = Failure("user does not have access to the " + view.name + " view") + + def permittedViews(user: Box[User]) : Set[View] = { + user match { + case Full(u) => u.permittedViews(this) + case _ =>{ + logger.info("no user was found in the permittedViews") + if(this.allowPublicAccess) Set(Public) else Set() + } + } + } + + def allowPublicAccess = allowAnnoymousAccess + + /** + * @param the view that we want test the access to + * @param the user that we want to see if he has access to the view or not + * @return true if the user is allowed to access this view, false otherwise + */ + def authorizedAccess(view: View, user: Option[User]) : Boolean = { + view match { + case Public => allowPublicAccess + case _ => user match { + case Some(u) => { + u.permittedViews(this).contains(view) + } + case None => false + } + } + } + + /** + * @param a user requesting to see the other users' permissions + * @return a Box of all the users' permissions of this bank account if the user passed as a parameter has access to the owner view (allowed to see this kind of data) + */ + def permissions(user : User) : Box[List[Permission]] = { + //check if the user have access to the owner view in this the account + if(authorizedAccess(Owner,Full(user))) + LocalStorage.permissions(this) + else + Failure("user : " + user.emailAddress + "don't have access to owner view on account " + id, Empty, Empty) + } + + /** + * @param a user that want to grant an other user access to a view + * @param the id of the view that we want to grant access + * @param the id of the other user that we want grant access + * @return a Full(true) if everything is okay, a Failure otherwise + */ + def addPermission(user : User, viewId : String, otherUserId : String) : Box[Boolean] = { + //check if the user have access to the owner view in this the account + if(authorizedAccess(Owner,Full(user))) + for{ + view <- View.fromUrl(viewId) //check if the viewId corresponds to a view + otherUser <- User.findById(otherUserId) //check if the userId corresponds to a user + isSaved <- LocalStorage.addPermission(id, view, otherUser) ?~ "could not save the privilege" + } yield isSaved + else + Failure("user : " + user.emailAddress + "don't have access to owner view on account " + id, Empty, Empty) + } + + /** + * @param a user that want to grant an other user access to a list views + * @param the list of views ids that we want to grant access + * @param the id of the other user that we want grant access + * @return a the list of the granted views if everything is okay, a Failure otherwise + */ + def addPermissions(user : User, viewIds : List[String], otherUserId : String) : Box[List[View]] = { + //we try to get all the views that correspond to that list of view ids + lazy val viewBoxes = viewIds.map(View.fromUrl) + //we see if the the is Failures + lazy val failureList = viewBoxes.collect(v => { + v match { + case x : Failure => x + } + }) + + lazy val viewsFormIds : Box[List[View]] = + //if no failures then we return the Full views + if(failureList.size == 0) + Full(viewBoxes.flatten) + else + //we return just the first failure + failureList.head + + //check if the user have access to the owner view in this the account + if(authorizedAccess(Owner,Full(user))) + for{ + otherUser <- User.findById(otherUserId) //check if the userId corresponds to a user + views <- viewsFormIds + grantedViews <- LocalStorage.addPermissions(id, views, otherUser) ?~ "could not save the privilege" + } yield views + else + Failure("user : " + user.emailAddress + "don't have access to owner view on account " + id, Empty, Empty) + } + + /** + * @param a user that want to revoke an other user access to a view + * @param the id of the view that we want to revoke access + * @param the id of the other user that we want revoke access + * @return a Full(true) if everything is okay, a Failure otherwise + */ + def revokePermission(user : User, viewId : String, otherUserId : String) : Box[Boolean] = { + //check if the user have access to the owner view in this the account + if(authorizedAccess(Owner,Full(user))) + for{ + view <- View.fromUrl(viewId) //check if the viewId corresponds to a view + otherUser <- User.findById(otherUserId) //check if the userId corresponds to a user + isRevoked <- LocalStorage.revokePermission(id, view, otherUser) ?~ "could not revoke the privilege" + } yield isRevoked + else + Failure("user : " + user.emailAddress + " don't have access to owner view on account " + id, Empty, Empty) + } + + /** + * + * @param a user that want to revoke an other user access to a view + * @param the id of the other user that we want revoke access + * @return a Full(true) if everything is okay, a Failure otherwise + */ + + def revokeAllPermission(user : User, otherUserId : String) : Box[Boolean] = { + //check if the user have access to the owner view in this the account + if(authorizedAccess(Owner,Full(user))) + for{ + otherUser <- User.findById(otherUserId) //check if the userId corresponds to a user + isRevoked <- LocalStorage.revokeAllPermission(id, otherUser) ?~ "could not revoke the privileges" + } yield isRevoked + else + Failure("user : " + user.emailAddress + " don't have access to owner view on account " + id, Empty, Empty) + } + + def views(user : User) : Box[List[View]] = { + //check if the user have access to the owner view in this the account + if(authorizedAccess(Owner,Full(user))) + for{ + isRevoked <- LocalStorage.views(id) ?~ "could not get the views" + } yield isRevoked + else + Failure("user : " + user.emailAddress + " don't have access to owner view on account " + id, Empty, Empty) + } + + def moderatedTransaction(id: String, view: View, user: Box[User]) : Box[ModeratedTransaction] = { + if(authorizedAccess(view, user)) + LocalStorage.getModeratedTransaction(id, bankPermalink, permalink)(view.moderate) + else + viewNotAllowed(view) + } + + def getModeratedTransactions(user : Box[User], view : View, queryParams: OBPQueryParam*): Box[List[ModeratedTransaction]] = { + if(authorizedAccess(view, user)) + LocalStorage.getModeratedTransactions(permalink, bankPermalink, queryParams: _*)(view.moderate) + else + viewNotAllowed(view) + } + + def moderatedBankAccount(view: View, user: Box[User]) : Box[ModeratedBankAccount] = { + if(authorizedAccess(view, user)) + //implicit conversion from option to box + view.moderate(this) + else + viewNotAllowed(view) + } + + /** + * @param the view that we will use to get the ModeratedOtherBankAccount list + * @param the user that want access to the ModeratedOtherBankAccount list + * @return a Box of a list ModeratedOtherBankAccounts, it the bank + * accounts that have at least one transaction in common with this bank account + */ + def moderatedOtherBankAccounts(view : View, user : Box[User]) : Box[List[ModeratedOtherBankAccount]] = { + if(authorizedAccess(view, user)) + LocalStorage.getModeratedOtherBankAccounts(id)(view.moderate) + else + viewNotAllowed(view) + } + /** + * @param the ID of the other bank account that the user want have access + * @param the view that we will use to get the ModeratedOtherBankAccount + * @param the user that want access to the otherBankAccounts list + * @return a Box of a ModeratedOtherBankAccounts, it a bank + * account that have at least one transaction in common with this bank account + */ + def moderatedOtherBankAccount(otherAccountID : String, view : View, user : Box[User]) : Box[ModeratedOtherBankAccount] = + if(authorizedAccess(view, user)) + LocalStorage.getModeratedOtherBankAccount(id, otherAccountID)(view.moderate) + else + viewNotAllowed(view) + + def overviewJson(user: Box[User]): JObject = { + val views = permittedViews(user) + ("number" -> number) ~ + ("account_alias" -> label) ~ + ("owner_description" -> "") ~ + ("views_available" -> views.map(view => view.toJson)) ~ + View.linksJson(views, permalink, bankPermalink) + } +} + +object BankAccount { + def apply(bankpermalink: String, bankAccountPermalink: String) : Box[BankAccount] = { + LocalStorage.getBankAccount(bankpermalink, bankAccountPermalink) + } + + def publicAccounts : List[BankAccount] = { + LocalStorage.getAllPublicAccounts() + } +} + +class OtherBankAccount( + val id : String, + val label : String, + val nationalIdentifier : String, + //the bank international identifier + val swift_bic : Option[String], + //the international account identifier + val iban : Option[String], + val number : String, + val bankName : String, + val metadata : OtherBankAccountMetadata, + val kind : String +) + +class Transaction( + //A universally unique id + val uuid : String, + //The bank's id for the transaction + val id : String, + val thisAccount : BankAccount, + val otherAccount : OtherBankAccount, + val metadata : TransactionMetadata, + //E.g. cash withdrawal, electronic payment, etc. + val transactionType : String, + val amount : BigDecimal, + //ISO 4217, e.g. EUR, GBP, USD, etc. + val currency : String, + // Bank provided comment + val label : Option[String], + // The date the transaction was initiated + val startDate : Date, + // The date when the money finished changing hands + val finishDate : Date, + //the new balance for the bank account + val balance : BigDecimal +) \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/Metadata.scala b/MavLift/src/main/scala/code/model/Metadata.scala new file mode 100644 index 000000000..faa129e38 --- /dev/null +++ b/MavLift/src/main/scala/code/model/Metadata.scala @@ -0,0 +1,199 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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.model +import java.util.Date +import java.net.URL +import net.liftweb.common.{Box,Full} +import net.liftweb.json.JsonAST.JObject +import net.liftweb.json.JsonDSL._ + + +trait Comment { + def id_ : String + // The person that posted the comment + def postedBy : Box[User] + + //the id of the view related to the comment + def viewId : Long + + // The actual text of the comment + def text : String + + def datePosted : Date + + //if this is a reply, the id of the original comment + def replyToID : String + + def toJson : JObject = { + val userInJson = postedBy match { + case Full(user) => user.toJson + case _ => ("id" -> "") ~ + ("provider" -> "") ~ + ("display_name" -> "") + } + + ("id" -> id_) ~ + ("date" -> datePosted.toString) ~ + ("comment" -> text) ~ + ("view" -> viewId) ~ + ("user" -> userInJson) ~ + ("reply_to" -> "") + } +} + +trait Tag { + + def id_ : String + def datePosted : Date + def postedBy : Box[User] + def viewId : Long + def value : String +} + +trait GeoTag { + + def datePosted : Date + def postedBy : Box[User] + def viewId : Long + def longitude : Double + def latitude : Double +} + +trait TransactionImage { + + def id_ : String + def datePosted : Date + def postedBy : Box[User] + def viewId : Long + def description : String + def imageUrl : URL +} + +class OtherBankAccountMetadata( + val publicAlias : String, + val privateAlias : String, + val moreInfo : String, + val url : String, + val imageURL : String, + val openCorporatesURL : String, + val corporateLocation : GeoTag, + val physicalLocation : GeoTag, + val addMoreInfo : (String) => Boolean, + val addURL : (String) => Boolean, + val addImageURL : (String) => Boolean, + val addOpenCorporatesURL : (String) => Boolean, + + /** + * @param: userId + * @param: viewId + * @param: datePosted + * @param: longitude + * @param: latitude + */ + val addCorporateLocation : (String, Long, Date, Double, Double) => Boolean, + val deleteCorporateLocation : () => Boolean, + /** + * @param: userId + * @param: viewId + * @param: datePosted + * @param: longitude + * @param: latitude + */ + val addPhysicalLocation : (String, Long, Date, Double, Double) => Boolean, + val deletePhysicalLocation : () => Boolean, + val addPublicAlias : (String) => Boolean, + val addPrivateAlias : (String) => Boolean +) + +class TransactionMetadata( + val ownerComment : String, + val addOwnerComment : String => Unit, + val comments: List[Comment], + /** + * @param: userId + * @param: viewId + * @param: text + * @param: datePosted + */ + val addComment : (String,Long, String, Date) => Comment, + /** + * @param: commentId + */ + val deleteComment : (String) => Box[Unit], + + val tags: List[Tag], + /** + * @param: userId + * @param: viewId + * @param: tag + * @param: datePosted + */ + val addTag: (String, Long, String, Date) => Tag, + /** + * @param: tagId + */ + val deleteTag : (String) => Box[Unit], + val images : List[TransactionImage], + /** + * @param: userId + * @param: viewId + * @param: description + * @param: datePosted + * @param: imageURL + */ + val addImage : (String, Long, String, Date, URL) => TransactionImage, + /** + * @param: imageId + */ + val deleteImage : String => Unit, + /** + * @param: userId + * @param: viewId + * @param: datePosted + * @param: longitude + * @param: latitude + */ + val whereTags : List[GeoTag], + /** + * @param: userId + * @param: viewId + * @param: datePosted + * @param: longitude + * @param: latitude + */ + val addWhereTag : (String, Long, Date, Double, Double) => Boolean, + /** + * @param: viewId + */ + val deleteWhereTag : (Long) => Boolean +) \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/ModeratedBankingData.scala b/MavLift/src/main/scala/code/model/ModeratedBankingData.scala new file mode 100644 index 000000000..eca7b049f --- /dev/null +++ b/MavLift/src/main/scala/code/model/ModeratedBankingData.scala @@ -0,0 +1,306 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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.model +import java.util.Date +import net.liftweb.json.JsonAST.JObject +import net.liftweb.json.JsonAST.JString +import net.liftweb.json.JsonAST.JField +import net.liftweb.json._ +import net.liftweb.json.JsonDSL._ +import net.liftweb.http.JsonResponse +import net.liftweb.http.LiftResponse +import java.text.SimpleDateFormat +import java.net.URL +import net.liftweb.common.Box +import net.liftweb.common.Full +import net.liftweb.common.Failure + + +class ModeratedTransaction( + val UUID : String, + val id: String, + val bankAccount: Option[ModeratedBankAccount], + val otherBankAccount: Option[ModeratedOtherBankAccount], + val metadata : Option[ModeratedTransactionMetadata], + val transactionType: Option[String], + val amount: Option[BigDecimal], + val currency: Option[String], + val label: Option[String], + val startDate: Option[Date], + val finishDate: Option[Date], + //the filteredBlance type in this class is a string rather than Big decimal like in Transaction trait for snippet (display) reasons. + //the view should be able to return a sign (- or +) or the real value. casting signs into bigdecimal is not possible + val balance : String +) { + + def dateOption2JString(date: Option[Date]) : JString = { + JString(date.map(d => ModeratedTransaction.dateFormat.format(d)) getOrElse "") + } + + def toJson(view: View): JObject = { + ("view" -> view.permalink) ~ + ("uuid" -> id) ~ + ("this_account" -> bankAccount) ~ + ("other_account" -> otherBankAccount) ~ + ("details" -> + ("type_en" -> transactionType) ~ //TODO: Need translations for transaction types and a way to + ("type_de" -> transactionType) ~ // figure out what language the original type is in + ("posted" -> dateOption2JString(startDate)) ~ + ("completed" -> dateOption2JString(finishDate)) ~ + ("new_balance" -> + ("currency" -> currency.getOrElse("")) ~ + ("amount" -> balance)) ~ + ("value" -> + ("currency" -> currency.getOrElse("")) ~ + ("amount" -> amount))) + } +} + +object ModeratedTransaction { + val dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") +} + +class ModeratedTransactionMetadata( + val ownerComment : Option[String], + val addOwnerComment : Option[(String => Unit)], + val comments : Option[List[Comment]], + val addComment: Option[(String, Long, String, Date) => Comment], + private val deleteComment: Option[(String) => Box[Unit]], + val tags : Option[List[Tag]], + val addTag : Option[(String, Long, String, Date) => Tag], + private val deleteTagFunc : Option[(String) => Box[Unit]], + val images : Option[List[TransactionImage]], + val addImage : Option[(String, Long, String, Date, URL) => TransactionImage], + private val deleteImageFunc : Option[String => Unit], + val whereTag : Option[GeoTag], + val addWhereTag : Option[(String, Long, Date, Double, Double) => Boolean], + private val deleteWhereTag : Option[(Long) => Boolean] +){ + + @deprecated //TODO:This should be removed once SoFi is split from the API + def deleteTag = deleteTagFunc + + /** + * @return Full if deleting the tag worked, or a failure message if it didn't + */ + def deleteTag(tagId : String, user: Option[User], bankAccount : BankAccount) : Box[Unit] = { + for { + tagList <- Box(tags) ?~ { "You must be able to see tags in order to delete them"} + tag <- Box(tagList.find(tag => tag.id_ == tagId)) ?~ {"Tag with id " + tagId + "not found for this transaction"} + deleteFunc <- if(tag.postedBy == user || bankAccount.authorizedAccess(Owner, user)) + Box(deleteTagFunc) ?~ "Deleting tags not permitted for this view" + else + Failure("deleting tags not permitted for the current user") + tagIsDeleted <- deleteFunc(tagId) + } yield { + } + } + + + @deprecated //This should be removed once SoFi is split from the API + def deleteImage = deleteImageFunc + + /** + * @return Full if deleting the image worked, or a failure message if it didn't + */ + def deleteImage(imageId : String, user: Option[User], bankAccount : BankAccount) : Box[Unit] = { + for { + imageList <- Box(images) ?~ { "You must be able to see images in order to delete them"} + image <- Box(imageList.find(image => image.id_ == imageId)) ?~ {"Image with id " + imageId + "not found for this transaction"} + deleteFunc <- if(image.postedBy == user || bankAccount.authorizedAccess(Owner, user)) + Box(deleteImageFunc) ?~ "Deleting images not permitted for this view" + else + Failure("Deleting images not permitted for the current user") + } yield { + deleteFunc(imageId) + } + } + + def deleteComment(commentId: String, user: Option[User],bankAccount: BankAccount) : Box[Unit] = { + for { + commentList <- Box(comments) ?~ {"You must be able to see comments in order to delete them"} + comment <- Box(commentList.find(comment => comment.id_ == commentId)) ?~ {"Comment with id "+commentId+" not found for this transaction"} + deleteFunc <- if(comment.postedBy == user || bankAccount.authorizedAccess(Owner, user)) + Box(deleteComment) ?~ "Deleting comments not permitted for this view" + else + Failure("Deleting comments not permitted for the current user") + } yield { + deleteFunc(commentId) + } + } + + def deleteWhereTag(viewId: Long, user: Option[User],bankAccount: BankAccount) : Box[Boolean] = { + for { + whereTag <- Box(whereTag) ?~ {"You must be able to see the where tag in order to delete it"} + deleteFunc <- if(whereTag.postedBy == user || bankAccount.authorizedAccess(Owner, user)) + Box(deleteWhereTag) ?~ "Deleting tag is not permitted for this view" + else + Failure("Deleting tags not permitted for the current user") + } yield { + deleteFunc(viewId) + } + } +} + + + +object ModeratedTransactionMetadata { + implicit def moderatedTransactionMetadata2Json(mTransactionMeta: ModeratedTransactionMetadata) : JObject = { + JObject(JField("blah", JString("test")) :: Nil) + } +} + +class ModeratedBankAccount( + val id : String, + val owners : Option[Set[AccountOwner]], + val accountType : Option[String], + val balance: String = "", + val currency : Option[String], + val label : Option[String], + val nationalIdentifier : Option[String], + val swift_bic : Option[String], + val iban : Option[String], + val number: Option[String], + val bankName: Option[String], + val bankPermalink : Option[String] +){ + def toJson = { + //TODO: Decide if unauthorized info (I guess that is represented by a 'none' option'? I can't really remember) + // should just disappear from the json or if an empty string should be used. + //I think we decided to use empty strings. What was the point of all the options again? + ("number" -> number.getOrElse("")) ~ + ("owners" -> owners.flatten.map(owner => + ("id" ->owner.id) ~ + ("name" -> owner.name))) ~ + ("type" -> accountType.getOrElse("")) ~ + ("balance" -> + ("currency" -> currency.getOrElse("")) ~ + ("amount" -> balance)) ~ + ("IBAN" -> iban.getOrElse("")) ~ + ("date_opened" -> "") + } +} + +object ModeratedBankAccount { + + def bankJson(holderName: String, isAlias : String, number: String, + kind: String, bankIBAN: String, bankNatIdent: String, + bankName: String) : JObject = { + ("holder" -> + ( + ("name" -> holderName) ~ + ("alias"-> isAlias) + ))~ + ("number" -> number) ~ + ("kind" -> kind) ~ + ("bank" -> + ("IBAN" -> bankIBAN) ~ + ("national_identifier" -> bankNatIdent) ~ + ("name" -> bankName)) + } + + implicit def moderatedBankAccount2Json(mBankAccount: ModeratedBankAccount) : JObject = { + val holderName = mBankAccount.owners match{ + case Some(ownersSet) => if(ownersSet.size!=0) + ownersSet.toList(0).name + else + "" + case _ => "" + } + val isAlias = "no" + val number = mBankAccount.number getOrElse "" + val kind = mBankAccount.accountType getOrElse "" + val bankIBAN = mBankAccount.iban.getOrElse("") + val bankNatIdent = mBankAccount.nationalIdentifier getOrElse "" + val bankName = mBankAccount.bankName getOrElse "" + bankJson(holderName, isAlias, number, kind, bankIBAN, bankNatIdent, bankName) + } +} + +class ModeratedOtherBankAccount( + val id : String, + val label : AccountName, + val nationalIdentifier : Option[String], + val swift_bic : Option[String], + val iban : Option[String], + val bankName : Option[String], + val number : Option[String], + val metadata : Option[ModeratedOtherBankAccountMetadata], + val kind : Option[String] +){ + + def isAlias : Boolean = label.aliasType match{ + case PublicAlias | PrivateAlias => true + case _ => false + } +} + +object ModeratedOtherBankAccount { + implicit def moderatedOtherBankAccount2Json(mOtherBank: ModeratedOtherBankAccount) : JObject = { + val holderName = mOtherBank.label.display + val isAlias = if(mOtherBank.isAlias) "yes" else "no" + val number = mOtherBank.number getOrElse "" + val kind = "" + val bankIBAN = mOtherBank.iban.getOrElse("") + val bankNatIdent = mOtherBank.nationalIdentifier getOrElse "" + val bankName = mOtherBank.bankName getOrElse "" + ModeratedBankAccount.bankJson(holderName, isAlias, number, kind, bankIBAN, bankNatIdent, bankName) + } +} + +class ModeratedOtherBankAccountMetadata( + val moreInfo : Option[String], + val url : Option[String], + val imageURL : Option[String], + val openCorporatesURL : Option[String], + val corporateLocation : Option[GeoTag], + val physicalLocation : Option[GeoTag], + val publicAlias : Option[String], + val privateAlias : Option[String], + val addMoreInfo : Option[(String) => Boolean], + val addURL : Option[(String) => Boolean], + val addImageURL : Option[(String) => Boolean], + val addOpenCorporatesURL : Option[(String) => Boolean], + val addCorporateLocation : Option[(String, Long, Date, Double, Double) => Boolean], + val addPhysicalLocation : Option[(String, Long, Date, Double, Double) => Boolean], + val addPublicAlias : Option[(String) => Boolean], + val addPrivateAlias : Option[(String) => Boolean], + val deleteCorporateLocation : Option[() => Boolean], + val deletePhysicalLocation : Option[() => Boolean] +) + +object ModeratedOtherBankAccountMetadata { + implicit def moderatedOtherBankAccountMetadata2Json(mOtherBankMeta: ModeratedOtherBankAccountMetadata) : JObject = { + JObject(JField("blah", JString("test")) :: Nil) + } +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/OAuth.scala b/MavLift/src/main/scala/code/model/OAuth.scala index 49d32be49..9d935c0a9 100644 --- a/MavLift/src/main/scala/code/model/OAuth.scala +++ b/MavLift/src/main/scala/code/model/OAuth.scala @@ -1,4 +1,4 @@ -/** +/** Open Bank Project - Transparency / Social Finance Web Application Copyright (C) 2011, 2012, TESOBE / Music Pictures Ltd @@ -15,14 +15,14 @@ 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 . -Email: contact@tesobe.com -TESOBE / Music Pictures Ltd +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd Osloerstrasse 16/17 Berlin 13359, Germany This product includes software developed at TESOBE (http://www.tesobe.com/) - by + by Simon Redfern : simon AT tesobe DOT com Stefan Bethge : stefan AT tesobe DOT com Everett Sochowski : everett AT tesobe DOT com @@ -33,9 +33,15 @@ package code.model import net.liftweb.mapper._ import java.util.Date import scala.compat.Platform -import code.model.dataAccess.OBPUser +import net.liftweb.http.SHtml +import net.liftweb.util.FieldError +import net.liftweb.util.FieldIdentifier +import net.liftweb.common.{Full,Failure,Box,Empty} +import code.model.dataAccess.Admin +import net.liftweb.util.Helpers +import Helpers.now -object AppType extends Enumeration("web", "mobile") +object AppType extends Enumeration("web", "mobile") { type AppType = Value val Web, Mobile = Value @@ -47,35 +53,64 @@ object TokenType extends Enumeration("request", "access") val Request, Access = Value } -class Consumer extends LongKeyedMapper[Consumer]{ +class Consumer extends LongKeyedMapper[Consumer] with CreatedUpdated{ def getSingleton = Consumer def primaryKeyField = id - object id extends MappedLongIndex(this) + object id extends MappedLongIndex(this) + + def minLength3(field: MappedString[Consumer])( s : String) = { + if(s.length() < 3) List(FieldError(field, {field.displayName + " must be at least 3 characters"})) + else Nil + } + object key extends MappedString(this, 250){ - override def dbIndexed_? = true - } + override def dbIndexed_? = true + } object secret extends MappedString(this, 250) object isActive extends MappedBoolean(this) object name extends MappedString(this, 100){ + override def validations = minLength3(this) _ :: super.validations override def dbIndexed_? = true - } - object appType extends MappedEnum(this,AppType) - object description extends MappedText(this) - object developerEmail extends MappedString(this, 100) - object insertDate extends MappedDateTime(this){ - override def defaultValue = new Date(Platform.currentTime) + override def displayName = "App name:" } - object updateDate extends MappedDateTime(this) + object appType extends MappedEnum(this,AppType) { + override def displayName = "App type:" + } + object description extends MappedText(this) { + override def displayName = "Description:" + } + object developerEmail extends MappedEmail(this, 100) { + def uniqueEmail(field: MappedEmail[Consumer])(s : String) = { + Consumer.find(By(Consumer.developerEmail, s)) match { + case Full(c) => List(FieldError(field, {"This email address is already registered."})) + case _ => Nil + } + } + override def displayName = "Email:" + override def validations = uniqueEmail(this) _ :: super.validations + } + } -object Consumer extends Consumer with LongKeyedMetaMapper[Consumer]{} +object Consumer extends Consumer with LongKeyedMetaMapper[Consumer] with CRUDify[Long, Consumer]{ + //list all path : /admin/consumer/list + override def calcPrefix = List("admin",_dbTableNameLC) + + override def editMenuLocParams = List(Admin.testLogginIn) + override def showAllMenuLocParams = List(Admin.testLogginIn) + override def deleteMenuLocParams = List(Admin.testLogginIn) + override def createMenuLocParams = List(Admin.testLogginIn) + override def viewMenuLocParams = List(Admin.testLogginIn) + + override def fieldOrder = List(name, appType, description, developerEmail) +} class Nonce extends LongKeyedMapper[Nonce] { - + def getSingleton = Nonce def primaryKeyField = id - object id extends MappedLongIndex(this) + object id extends MappedLongIndex(this) object consumerkey extends MappedString(this, 250) //we store the consumer Key and we don't need to keep a reference to the token consumer as foreign key object tokenKey extends MappedString(this, 250){ //we store the token Key and we don't need to keep a reference to the token object as foreign key override def defaultValue = "" @@ -87,24 +122,46 @@ class Nonce extends LongKeyedMapper[Nonce] { } } object value extends MappedString(this,250) - + } object Nonce extends Nonce with LongKeyedMetaMapper[Nonce]{} class Token extends LongKeyedMapper[Token]{ - def getSingleton = Token + def getSingleton = Token def primaryKeyField = id - object id extends MappedLongIndex(this) //TODO : auto increment + object id extends MappedLongIndex(this) object tokenType extends MappedEnum(this, TokenType) object consumerId extends MappedLongForeignKey(this, Consumer) - object userId extends MappedLongForeignKey(this, OBPUser) + object userId extends MappedString(this,255) object key extends MappedString(this,250) object secret extends MappedString(this,250) object callbackURL extends MappedString(this,250) object verifier extends MappedString(this,250) - object duration extends MappedLong(this)//expressed in milliseconds + object duration extends MappedLong(this)//expressed in milliseconds object expirationDate extends MappedDateTime(this) object insertDate extends MappedDateTime(this) + def user = User.findById(userId.get) + def isValid : Boolean = expirationDate.is after now + private def fiveRandomNumbers() : String = { + def r() = Helpers.randomInt(9).toString //from zero to 9 + (1 to 5).map(x => r()).foldLeft("")(_ + _) + } + def gernerateVerifier : String = + if (verifier.isEmpty) + { + val generatedVerifier = fiveRandomNumbers() + verifier(generatedVerifier).save + generatedVerifier + } + else + verifier.is +} +object Token extends Token with LongKeyedMetaMapper[Token]{ + def gernerateVerifier(key : String) : Box[String] = { + Token.find(key) match { + case Full(tkn) => Full(tkn.gernerateVerifier) + case _ => Failure("Token not found",Empty, Empty) + } + } } -object Token extends Token with LongKeyedMetaMapper[Token]{} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/traits/User.scala b/MavLift/src/main/scala/code/model/User.scala similarity index 66% rename from MavLift/src/main/scala/code/model/traits/User.scala rename to MavLift/src/main/scala/code/model/User.scala index 5e1b31efc..12a3f0ad7 100644 --- a/MavLift/src/main/scala/code/model/traits/User.scala +++ b/MavLift/src/main/scala/code/model/User.scala @@ -1,4 +1,4 @@ -/** +/** Open Bank Project - Transparency / Social Finance Web Application Copyright (C) 2011, 2012, TESOBE / Music Pictures Ltd @@ -15,14 +15,14 @@ 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 . -Email: contact@tesobe.com -TESOBE / Music Pictures Ltd +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd Osloerstrasse 16/17 Berlin 13359, Germany This product includes software developed at TESOBE (http://www.tesobe.com/) - by + by Simon Redfern : simon AT tesobe DOT com Stefan Bethge : stefan AT tesobe DOT com Everett Sochowski : everett AT tesobe DOT com @@ -30,21 +30,37 @@ Berlin 13359, Germany */ -package code.model.traits +package code.model import net.liftweb.json.JsonDSL._ import net.liftweb.json.JsonAST.JObject +import code.model.dataAccess.LocalStorage +import net.liftweb.common.Box trait User { def id_ : String + def provider : String def emailAddress : String - def theFistName : String + def theFirstName : String def theLastName : String def permittedViews(bankAccount: BankAccount) : Set[View] def hasMangementAccess(bankAccount: BankAccount) : Boolean - def accountsWithMoreThanAnonAccess : Set[BankAccount] - def toJson : JObject = + override def toString = emailAddress + + /** + * @return the bank accounts where the user has at least access to a non public view (is_public==false) + */ + def nonPublicAccounts : Box[List[BankAccount]] = LocalStorage.getNonPublicBankAccounts(this) + + def toJson : JObject = ("id" -> id_) ~ ("provider" -> "sofi.openbankproject.com") ~ - ("display_name" -> {theFistName + " " + theLastName}) + ("display_name" -> {theFirstName + " " + theLastName}) +} + +object User { + def findById(id : String) : Box[User] = + LocalStorage.getUser(id) + def currentUser : Box[User] = + LocalStorage.getCurrentUser } \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/View.scala b/MavLift/src/main/scala/code/model/View.scala new file mode 100644 index 000000000..da655ac70 --- /dev/null +++ b/MavLift/src/main/scala/code/model/View.scala @@ -0,0 +1,962 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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.model + +import net.liftweb.http.SHtml +import net.liftweb.json.JsonDSL._ +import net.liftweb.json.JsonAST.JObject +import net.liftweb.common.{Box, Empty, Full, Failure} +import java.util.Date + + +class AliasType +class Alias extends AliasType +object PublicAlias extends Alias +object PrivateAlias extends Alias +object NoAlias extends AliasType +case class AccountName(display: String, aliasType: AliasType) +case class Permission( + user : User, + views : List[View] +) + +trait View { + + //e.g. "Public", "Authorities", "Our Network", etc. + def id: Long + def name: String + def description : String + def permalink : String + def isPublic : Boolean + + //the view settings + def usePrivateAliasIfOneExists: Boolean + def usePublicAliasIfOneExists: Boolean + + //reading access + + //transaction fields + def canSeeTransactionThisBankAccount : Boolean + def canSeeTransactionOtherBankAccount : Boolean + def canSeeTransactionMetadata : Boolean + def canSeeTransactionLabel: Boolean + def canSeeTransactionAmount: Boolean + def canSeeTransactionType: Boolean + def canSeeTransactionCurrency: Boolean + def canSeeTransactionStartDate: Boolean + def canSeeTransactionFinishDate: Boolean + def canSeeTransactionBalance: Boolean + + //transaction metadata + def canSeeComments: Boolean + def canSeeOwnerComment: Boolean + def canSeeTags : Boolean + def canSeeImages : Boolean + + //Bank account fields + def canSeeBankAccountOwners : Boolean + def canSeeBankAccountType : Boolean + def canSeeBankAccountBalance : Boolean + def canSeeBankAccountBalancePositiveOrNegative : Boolean + def canSeeBankAccountCurrency : Boolean + def canSeeBankAccountLabel : Boolean + def canSeeBankAccountNationalIdentifier : Boolean + def canSeeBankAccountSwift_bic : Boolean + def canSeeBankAccountIban : Boolean + def canSeeBankAccountNumber : Boolean + def canSeeBankAccountBankName : Boolean + def canSeeBankAccountBankPermalink : Boolean + + //other bank account fields + def canSeeOtherAccountNationalIdentifier : Boolean + def canSeeSWIFT_BIC : Boolean + def canSeeOtherAccountIBAN : Boolean + def canSeeOtherAccountBankName : Boolean + def canSeeOtherAccountNumber : Boolean + def canSeeOtherAccountMetadata : Boolean + def canSeeOtherAccountKind : Boolean + + //other bank account meta data + def canSeeMoreInfo: Boolean + def canSeeUrl: Boolean + def canSeeImageUrl: Boolean + def canSeeOpenCorporatesUrl: Boolean + def canSeeCorporateLocation : Boolean + def canSeePhysicalLocation : Boolean + def canSeePublicAlias : Boolean + def canSeePrivateAlias : Boolean + def canAddMoreInfo : Boolean + def canAddURL : Boolean + def canAddImageURL : Boolean + def canAddOpenCorporatesUrl : Boolean + def canAddCorporateLocation : Boolean + def canAddPhysicalLocation : Boolean + def canAddPublicAlias : Boolean + def canAddPrivateAlias : Boolean + def canDeleteCorporateLocation : Boolean + def canDeletePhysicalLocation : Boolean + + //writing access + def canEditOwnerComment: Boolean + def canAddComment : Boolean + def canDeleteComment: Boolean + def canAddTag : Boolean + def canDeleteTag : Boolean + def canAddImage : Boolean + def canDeleteImage : Boolean + def canAddWhereTag : Boolean + def canSeeWhereTag : Boolean + def canDeleteWhereTag : Boolean + + // In the future we can add a method here to allow someone to show only transactions over a certain limit + + def moderate(transaction: Transaction): ModeratedTransaction = { + //transaction data + val transactionId = transaction.id + val transactionUUID = transaction.uuid + val thisBankAccount = moderate(transaction.thisAccount) + val otherBankAccount = moderate(transaction.otherAccount) + + //transation metadata + val transactionMetadata = + if(canSeeTransactionMetadata) + { + val ownerComment = if (canSeeOwnerComment) Some(transaction.metadata.ownerComment) else None + val comments = + if (canSeeComments) + Some(transaction.metadata.comments.filter(comment => comment.viewId==id)) + else None + val addCommentFunc= if(canAddComment) Some(transaction.metadata.addComment) else None + val deleteCommentFunc = + if(canDeleteComment) + Some(transaction.metadata.deleteComment) + else + None + val addOwnerCommentFunc:Option[String=> Unit] = if (canEditOwnerComment) Some(transaction.metadata.addOwnerComment) else None + val tags = + if(canSeeTags) + Some(transaction.metadata.tags.filter(_.viewId==id)) + else None + val addTagFunc = + if(canAddTag) + Some(transaction.metadata.addTag) + else + None + val deleteTagFunc = + if(canDeleteTag) + Some(transaction.metadata.deleteTag) + else + None + val images = + if(canSeeImages) Some(transaction.metadata.images.filter(_.viewId == id)) + else None + + val addImageFunc = + if(canAddImage) Some(transaction.metadata.addImage) + else None + + val deleteImageFunc = + if(canDeleteImage) Some(transaction.metadata.deleteImage) + else None + + val whereTag = + if(canSeeWhereTag) + transaction.metadata.whereTags.find(tag => tag.viewId == id) + else + None + + val addWhereTagFunc : Option[(String, Long, Date, Double, Double) => Boolean] = + if(canAddWhereTag) + Some(transaction.metadata.addWhereTag) + else + Empty + + val deleteWhereTagFunc : Option[(Long) => Boolean] = + if (canDeleteWhereTag) + Some(transaction.metadata.deleteWhereTag) + else + Empty + + + new Some( + new ModeratedTransactionMetadata( + ownerComment, + addOwnerCommentFunc, + comments, + addCommentFunc, + deleteCommentFunc, + tags, + addTagFunc, + deleteTagFunc, + images, + addImageFunc, + deleteImageFunc, + whereTag, + addWhereTagFunc, + deleteWhereTagFunc + )) + } + else + None + + val transactionType = + if (canSeeTransactionType) Some(transaction.transactionType) + else None + + val transactionAmount = + if (canSeeTransactionAmount) Some(transaction.amount) + else None + + val transactionCurrency = + if (canSeeTransactionCurrency) Some(transaction.currency) + else None + + val transactionLabel = + if (canSeeTransactionLabel) transaction.label + else None + + val transactionStartDate = + if (canSeeTransactionStartDate) Some(transaction.startDate) + else None + + val transactionFinishDate = + if (canSeeTransactionFinishDate) Some(transaction.finishDate) + else None + + val transactionBalance = + if (canSeeTransactionBalance) transaction.balance.toString() + else "" + + new ModeratedTransaction(transactionUUID, transactionId, thisBankAccount, otherBankAccount, transactionMetadata, + transactionType, transactionAmount, transactionCurrency, transactionLabel, transactionStartDate, + transactionFinishDate, transactionBalance) + } + + def moderate(bankAccount: BankAccount) : Option[ModeratedBankAccount] = { + if(canSeeTransactionThisBankAccount) + { + val owners : Set[AccountOwner] = if(canSeeBankAccountOwners) bankAccount.owners else Set() + val balance = + if(canSeeBankAccountBalance){ + bankAccount.balance.toString + } else if(canSeeBankAccountBalancePositiveOrNegative) { + if(bankAccount.balance.toString.startsWith("-")) "-" else "+" + } else "" + val accountType = if(canSeeBankAccountType) Some(bankAccount.accountType) else None + val currency = if(canSeeBankAccountCurrency) Some(bankAccount.currency) else None + val label = if(canSeeBankAccountLabel) Some(bankAccount.label) else None + val nationalIdentifier = if(canSeeBankAccountNationalIdentifier) Some(bankAccount.label) else None + val swiftBic = if(canSeeBankAccountSwift_bic) bankAccount.swift_bic else None + val iban = if(canSeeBankAccountIban) bankAccount.iban else None + val number = if(canSeeBankAccountNumber) Some(bankAccount.number) else None + val bankName = if(canSeeBankAccountBankName) Some(bankAccount.bankName) else None + val bankPermalink = if(canSeeBankAccountBankPermalink) Some(bankAccount.bankPermalink) else None + + Some( + new ModeratedBankAccount( + id = bankAccount.permalink, + owners = Some(owners), + accountType = accountType, + balance = balance, + currency = currency, + label = label, + nationalIdentifier = nationalIdentifier, + swift_bic = swiftBic, + iban = iban, + number = number, + bankName = bankName, + bankPermalink = bankPermalink + )) + } + else + None + } + + def moderate(otherBankAccount : OtherBankAccount) : Option[ModeratedOtherBankAccount] = { + if (canSeeTransactionOtherBankAccount) + { + //other account data + val otherAccountId = otherBankAccount.id + val otherAccountLabel: AccountName = { + val realName = otherBankAccount.label + if (usePublicAliasIfOneExists) { + + val publicAlias = otherBankAccount.metadata.publicAlias + + if (! publicAlias.isEmpty ) AccountName(publicAlias, PublicAlias) + else AccountName(realName, NoAlias) + + } else if (usePrivateAliasIfOneExists) { + + val privateAlias = otherBankAccount.metadata.privateAlias + + if (! privateAlias.isEmpty) AccountName(privateAlias, PrivateAlias) + else AccountName(realName, PrivateAlias) + } else + AccountName(realName, NoAlias) + } + val otherAccountNationalIdentifier = if (canSeeOtherAccountNationalIdentifier) Some(otherBankAccount.nationalIdentifier) else None + val otherAccountSWIFT_BIC = if (canSeeSWIFT_BIC) otherBankAccount.swift_bic else None + val otherAccountIBAN = if(canSeeOtherAccountIBAN) otherBankAccount.iban else None + val otherAccountBankName = if(canSeeOtherAccountBankName) Some(otherBankAccount.bankName) else None + val otherAccountNumber = if(canSeeOtherAccountNumber) Some(otherBankAccount.number) else None + val otherAccountKind = if(canSeeOtherAccountKind) Some(otherBankAccount.kind) else None + val otherAccountMetadata = + if(canSeeOtherAccountMetadata) + { + //other bank account metadata + val moreInfo = + if (canSeeMoreInfo) Some(otherBankAccount.metadata.moreInfo) + else None + val url = + if (canSeeUrl) Some(otherBankAccount.metadata.url) + else None + val imageUrl = + if (canSeeImageUrl) Some(otherBankAccount.metadata.imageURL) + else None + val openCorporatesUrl = + if (canSeeOpenCorporatesUrl) Some(otherBankAccount.metadata.openCorporatesURL) + else None + val corporateLocation : Option[GeoTag] = + if(canSeeCorporateLocation) + Some(otherBankAccount.metadata.corporateLocation) + else + None + val physicalLocation : Option[GeoTag] = + if(canSeePhysicalLocation) + Some(otherBankAccount.metadata.physicalLocation) + else + None + val addMoreInfo = + if(canAddMoreInfo) + Some(otherBankAccount.metadata.addMoreInfo) + else + None + val addURL = + if(canAddURL) + Some(otherBankAccount.metadata.addURL) + else + None + val addImageURL = + if(canAddImageURL) + Some(otherBankAccount.metadata.addImageURL) + else + None + val addOpenCorporatesUrl = + if(canAddOpenCorporatesUrl) + Some(otherBankAccount.metadata.addOpenCorporatesURL) + else + None + val addCorporateLocation = + if(canAddCorporateLocation) + Some(otherBankAccount.metadata.addCorporateLocation) + else + None + val addPhysicalLocation = + if(canAddPhysicalLocation) + Some(otherBankAccount.metadata.addPhysicalLocation) + else + None + val publicAlias = + if(canSeePublicAlias) + Some(otherBankAccount.metadata.publicAlias) + else + None + val privateAlias = + if(canSeePrivateAlias) + Some(otherBankAccount.metadata.privateAlias) + else + None + val addPublicAlias = + if(canAddPublicAlias) + Some(otherBankAccount.metadata.addPublicAlias) + else + None + val addPrivateAlias = + if(canAddPrivateAlias) + Some(otherBankAccount.metadata.addPrivateAlias) + else + None + val deleteCorporateLocation = + if(canDeleteCorporateLocation) + Some(otherBankAccount.metadata.deleteCorporateLocation) + else + None + val deletePhysicalLocation= + if(canDeletePhysicalLocation) + Some(otherBankAccount.metadata.deletePhysicalLocation) + else + None + + + Some( + new ModeratedOtherBankAccountMetadata( + moreInfo, + url, + imageUrl, + openCorporatesUrl, + corporateLocation, + physicalLocation, + publicAlias, + privateAlias, + addMoreInfo, + addURL, + addImageURL, + addOpenCorporatesUrl, + addCorporateLocation, + addPhysicalLocation, + addPublicAlias, + addPrivateAlias, + deleteCorporateLocation, + deletePhysicalLocation + )) + } + else + None + + Some( + new ModeratedOtherBankAccount( + otherAccountId, + otherAccountLabel, + otherAccountNationalIdentifier, + otherAccountSWIFT_BIC, + otherAccountIBAN, + otherAccountBankName, + otherAccountNumber, + otherAccountMetadata, + otherAccountKind)) + } + else + None + } + + def toJson : JObject = { + ("name" -> name) ~ + ("description" -> description) + } + +} + +//An implementation that has the least amount of permissions possible +class BaseView extends View { + def id = 1 + def name = "Restricted" + def permalink = "restricted" + def description = "" + def isPublic = false + + //the view settings + def usePrivateAliasIfOneExists = true + def usePublicAliasIfOneExists = true + + //reading access + + //transaction fields + def canSeeTransactionThisBankAccount = false + def canSeeTransactionOtherBankAccount = false + def canSeeTransactionMetadata = false + def canSeeTransactionLabel = false + def canSeeTransactionAmount = false + def canSeeTransactionType = false + def canSeeTransactionCurrency = false + def canSeeTransactionStartDate = false + def canSeeTransactionFinishDate = false + def canSeeTransactionBalance = false + + //transaction metadata + def canSeeComments = false + def canSeeOwnerComment = false + def canSeeTags = false + def canSeeImages = false + + //Bank account fields + def canSeeBankAccountOwners = false + def canSeeBankAccountType = false + def canSeeBankAccountBalance = false + def canSeeBankAccountBalancePositiveOrNegative = false + def canSeeBankAccountCurrency = false + def canSeeBankAccountLabel = false + def canSeeBankAccountNationalIdentifier = false + def canSeeBankAccountSwift_bic = false + def canSeeBankAccountIban = false + def canSeeBankAccountNumber = false + def canSeeBankAccountBankName = false + def canSeeBankAccountBankPermalink = false + + //other bank account fields + def canSeeOtherAccountNationalIdentifier = false + def canSeeSWIFT_BIC = false + def canSeeOtherAccountIBAN = false + def canSeeOtherAccountBankName = false + def canSeeOtherAccountNumber = false + def canSeeOtherAccountMetadata = false + def canSeeOtherAccountKind = false + + //other bank account meta data + def canSeeMoreInfo = false + def canSeeUrl = false + def canSeeImageUrl = false + def canSeeOpenCorporatesUrl = false + def canSeeCorporateLocation = false + def canSeePhysicalLocation = false + def canSeePublicAlias = false + def canSeePrivateAlias = false + + def canAddMoreInfo = false + def canAddURL = false + def canAddImageURL = false + def canAddOpenCorporatesUrl = false + def canAddCorporateLocation = false + def canAddPhysicalLocation = false + def canAddPublicAlias = false + def canAddPrivateAlias = false + def canDeleteCorporateLocation = false + def canDeletePhysicalLocation = false + + //writing access + def canEditOwnerComment = false + def canAddComment = false + def canDeleteComment = false + def canAddTag = false + def canDeleteTag = false + def canAddImage = false + def canDeleteImage = false + def canSeeWhereTag = false + def canAddWhereTag = false + def canDeleteWhereTag = false +} + +class FullView extends View { + def id = 2 + def name = "Full" + def permalink ="full" + def description = "" + def isPublic = false + + //the view settings + def usePrivateAliasIfOneExists = false + def usePublicAliasIfOneExists = false + + //reading access + + //transaction fields + def canSeeTransactionThisBankAccount = true + def canSeeTransactionOtherBankAccount = true + def canSeeTransactionMetadata = true + def canSeeTransactionLabel = true + def canSeeTransactionAmount = true + def canSeeTransactionType = true + def canSeeTransactionCurrency = true + def canSeeTransactionStartDate = true + def canSeeTransactionFinishDate = true + def canSeeTransactionBalance = true + + //transaction metadata + def canSeeComments = true + def canSeeOwnerComment = true + def canSeeTags = true + def canSeeImages = true + + //Bank account fields + def canSeeBankAccountOwners = true + def canSeeBankAccountType = true + def canSeeBankAccountBalance = true + def canSeeBankAccountBalancePositiveOrNegative = true + def canSeeBankAccountCurrency = true + def canSeeBankAccountLabel = true + def canSeeBankAccountNationalIdentifier = true + def canSeeBankAccountSwift_bic = true + def canSeeBankAccountIban = true + def canSeeBankAccountNumber = true + def canSeeBankAccountBankName = true + def canSeeBankAccountBankPermalink = true + + //other bank account fields + def canSeeOtherAccountNationalIdentifier = true + def canSeeSWIFT_BIC = true + def canSeeOtherAccountIBAN = true + def canSeeOtherAccountMetadata = true + def canSeeOtherAccountBankName = true + def canSeeOtherAccountNumber = true + def canSeeOtherAccountKind = true + + //other bank account meta data + def canSeeMoreInfo = true + def canSeeUrl = true + def canSeeImageUrl = true + def canSeeOpenCorporatesUrl = true + def canSeeCorporateLocation = true + def canSeePhysicalLocation = true + def canSeePublicAlias = true + def canSeePrivateAlias = true + + def canAddMoreInfo = true + def canAddURL = true + def canAddImageURL = true + def canAddOpenCorporatesUrl = true + def canAddCorporateLocation = true + def canAddPhysicalLocation = true + def canAddPublicAlias = true + def canAddPrivateAlias = true + def canDeleteCorporateLocation = true + def canDeletePhysicalLocation = true + + //writing access + def canEditOwnerComment = true + def canAddComment = true + def canDeleteComment = true + def canAddTag = true + def canDeleteTag = true + def canAddImage = true + def canDeleteImage = true + def canSeeWhereTag = true + def canAddWhereTag = true + def canDeleteWhereTag = true +} + + +object View { + //transform the url into a view + //TODO : load the view from the Data base + def fromUrl(viewNameURL: String): Box[View] = + viewNameURL match { + case "authorities" => Full(Authorities) + case "board" => Full(Board) + case "our-network" => Full(OurNetwork) + case "team" => Full(Team) + case "owner" => Full(Owner) + case "public" | "anonymous" => Full(Public) + case "management" => Full(Management) + case _ => Failure("view " + viewNameURL + " not found", Empty, Empty) + } + + def linksJson(views: Set[View], accountPermalink: String, bankPermalink: String): JObject = { + val viewsJson = views.map(view => { + ("rel" -> "account") ~ + ("href" -> { "/" + bankPermalink + "/account/" + accountPermalink + "/" + view.permalink }) ~ + ("method" -> "GET") ~ + ("title" -> "Get information about one account") + }) + + ("links" -> viewsJson) + } +} + +object Team extends FullView { + override def id = 3 + override def name = "Team" + override def permalink = "team" + override def description = "A view for team members related to the account. E.g. for a company bank account -> employees/contractors" + override def canEditOwnerComment= false + +} +object Board extends FullView { + override def id = 4 + override def name = "Board" + override def permalink = "board" + override def description = "A view for board members of a company to view that company's account data." + override def canEditOwnerComment= false +} +object Authorities extends FullView { + override def id = 5 + override def name = "Authorities" + override def permalink = "authorities" + override def description = "A view for authorities such as tax officials to view an account's data" + override def canEditOwnerComment= false +} + +object Public extends BaseView { + //the actual class extends the BaseView but in fact it does not matters be cause we don't care about the values + //of the canSeeMoreInfo, canSeeUrl,etc attributes and we implement a specific moderate method + + /** + * Current rules: + * + * If Public, and a public alias exists : Show the public alias + * If Public, and no public alias exists : Show the real account holder + * If our network, and a private alias exists : Show the private alias + * If our network, and no private alias exists : Show the real account holder + */ + override def id = 6 + override def name = "Public" + override def permalink = "public" + override def description = "A view of the account accessible by anyone." + override def isPublic = true + + + //Bank account fields + override def canSeeBankAccountOwners = true + override def canSeeBankAccountType = true + override def canSeeBankAccountBalancePositiveOrNegative = true + override def canSeeBankAccountCurrency = true + override def canSeeBankAccountLabel = true + override def canSeeBankAccountNationalIdentifier = true + override def canSeeBankAccountSwift_bic = true + override def canSeeBankAccountIban = true + override def canSeeBankAccountNumber = true + override def canSeeBankAccountBankName = true + + override def moderate(transaction: Transaction): ModeratedTransaction = { + + val transactionId = transaction.id + val transactionUUID = transaction.uuid + val accountBalance = "" //not used when displaying transactions, but we might eventually need it. if so, we need a ref to + //the bank account so we could do something like if(canSeeBankAccountBalance) bankAccount.balance else if + // canSeeBankAccountBalancePositiveOrNegative {show + or -} else "" + val thisBankAccount = moderate(transaction.thisAccount) + val otherBankAccount = moderate(transaction.otherAccount) + val transactionMetadata = + Some( + new ModeratedTransactionMetadata( + Some(transaction.metadata.ownerComment), + None, + Some(transaction.metadata.comments.filter(comment => comment.viewId==id)), + Some(transaction.metadata.addComment), + Some(transaction.metadata.deleteComment), + Some(transaction.metadata.tags.filter(_.viewId==id)), + Some(transaction.metadata.addTag), + Some(transaction.metadata.deleteTag), + Some(transaction.metadata.images.filter(_.viewId==id)), //TODO: Better if image takes a view as a parameter? + Some(transaction.metadata.addImage), + Some(transaction.metadata.deleteImage), + transaction.metadata.whereTags.find(tag => tag.viewId == id), + Some(transaction.metadata.addWhereTag), + Some(transaction.metadata.deleteWhereTag) + )) + + val transactionType = Some(transaction.transactionType) + val transactionAmount = Some(transaction.amount) + val transactionCurrency = Some(transaction.currency) + val transactionLabel = None + val transactionStartDate = Some(transaction.startDate) + val transactionFinishDate = Some(transaction.finishDate) + val transactionBalance = if (transaction.balance.toString().startsWith("-")) "-" else "+" + + new ModeratedTransaction( + transactionUUID, + transactionId, + thisBankAccount, + otherBankAccount, + transactionMetadata, + transactionType, + transactionAmount, + transactionCurrency, + transactionLabel, + transactionStartDate, + transactionFinishDate, + transactionBalance + ) + } + override def moderate(bankAccount: BankAccount) : Option[ModeratedBankAccount] = { + Some( + new ModeratedBankAccount( + id = bankAccount.permalink, + owners = Some(bankAccount.owners), + accountType = Some(bankAccount.accountType), + currency = Some(bankAccount.currency), + label = Some(bankAccount.label), + nationalIdentifier = None, + swift_bic = None, + iban = None, + number = Some(bankAccount.number), + bankName = Some(bankAccount.bankName), + bankPermalink = Some(bankAccount.bankPermalink) + ) + ) + } + override def moderate(otherAccount : OtherBankAccount) : Option[ModeratedOtherBankAccount] = { + val otherAccountLabel = { + val publicAlias = otherAccount.metadata.publicAlias + if(publicAlias.isEmpty) + AccountName(otherAccount.label, NoAlias) + else + AccountName(publicAlias, PublicAlias) + } + val otherAccountMetadata = { + def isPublicAlias = otherAccountLabel.aliasType match { + case PublicAlias => true + case _ => false + } + val moreInfo = if (isPublicAlias) None else Some(otherAccount.metadata.moreInfo) + val url = if (isPublicAlias) None else Some(otherAccount.metadata.url) + val imageUrl = if (isPublicAlias) None else Some(otherAccount.metadata.imageURL) + val openCorporatesUrl = if (isPublicAlias) None else Some(otherAccount.metadata.openCorporatesURL) + val corporateLocation = if (isPublicAlias) None else Some(otherAccount.metadata.corporateLocation) + val physicalLocation = if (isPublicAlias) None else Some(otherAccount.metadata.physicalLocation) + + Some( + new ModeratedOtherBankAccountMetadata( + moreInfo, + url, + imageUrl, + openCorporatesUrl, + corporateLocation, + physicalLocation, + Some(otherAccount.metadata.publicAlias), + None, + None, + None, + None, + None, + Some(otherAccount.metadata.addCorporateLocation), + Some(otherAccount.metadata.addPhysicalLocation), + None, + None, + Some(otherAccount.metadata.deleteCorporateLocation), + Some(otherAccount.metadata.deletePhysicalLocation) + )) + } + + Some( + new ModeratedOtherBankAccount( + otherAccount.id, + otherAccountLabel, + None, + None, + None, + None, + None, + otherAccountMetadata, + None)) + } +} + +object OurNetwork extends BaseView { + override def id = 7 + override def name = "Our Network" + override def permalink ="our-network" + override def description = "A view for people related to the account in some way. E.g. for a company account this could include investors" + + " or current/potential clients" + override def moderate(transaction: Transaction): ModeratedTransaction = { + val transactionId = transaction.id + val transactionUUID = transaction.uuid + val accountBalance = "" //not used when displaying transactions, but we might eventually need it. if so, we need a ref to + //the bank account so we could do something like if(canSeeBankAccountBalance) bankAccount.balance else if + // canSeeBankAccountBalancePositiveOrNegative {show + or -} else "" + val thisBankAccount = moderate(transaction.thisAccount) + val otherBankAccount = moderate(transaction.otherAccount) + val transactionMetadata = + Some( + new ModeratedTransactionMetadata( + Some(transaction.metadata.ownerComment), + None, + Some(transaction.metadata.comments.filter(comment => comment.viewId==id)), + Some(transaction.metadata.addComment), + Some(transaction.metadata.deleteComment), + Some(transaction.metadata.tags.filter(_.viewId==id)), + Some(transaction.metadata.addTag), + Some(transaction.metadata.deleteTag), + Some(transaction.metadata.images.filter(_.viewId==id)), //TODO: Better if image takes a view as a parameter? + Some(transaction.metadata.addImage), + Some(transaction.metadata.deleteImage), + transaction.metadata.whereTags.find(tag => tag.viewId == id), + Some(transaction.metadata.addWhereTag), + Some(transaction.metadata.deleteWhereTag) + )) + val transactionType = Some(transaction.transactionType) + val transactionAmount = Some(transaction.amount) + val transactionCurrency = Some(transaction.currency) + val transactionLabel = transaction.label + val transactionStartDate = Some(transaction.startDate) + val transactionFinishDate = Some(transaction.finishDate) + val transactionBalance = transaction.balance.toString() + + new ModeratedTransaction(transactionUUID, transactionId, thisBankAccount, otherBankAccount, transactionMetadata, + transactionType, transactionAmount, transactionCurrency, transactionLabel, transactionStartDate, + transactionFinishDate, transactionBalance) + } + override def moderate(bankAccount: BankAccount) : Option[ModeratedBankAccount] = { + Some( + new ModeratedBankAccount( + id = bankAccount.permalink, + owners = Some(bankAccount.owners), + accountType = Some(bankAccount.accountType), + currency = Some(bankAccount.currency), + label = Some(bankAccount.label), + nationalIdentifier = None, + swift_bic = None, + iban = None, + number = Some(bankAccount.number), + bankName = Some(bankAccount.bankName), + bankPermalink = Some(bankAccount.bankPermalink) + ) + ) + } + override def moderate(otherAccount : OtherBankAccount) : Option[ModeratedOtherBankAccount] = { + val otherAccountLabel = { + val privateAlias = otherAccount.metadata.privateAlias + if(privateAlias.isEmpty) + AccountName(otherAccount.label, NoAlias) + else + AccountName(privateAlias, PrivateAlias) + } + val otherAccountMetadata = + Some( + new ModeratedOtherBankAccountMetadata( + Some(otherAccount.metadata.moreInfo), + Some(otherAccount.metadata.url), + Some(otherAccount.metadata.imageURL), + Some(otherAccount.metadata.openCorporatesURL), + Some(otherAccount.metadata.corporateLocation), + Some(otherAccount.metadata.physicalLocation), + Some(otherAccount.metadata.publicAlias), + Some(otherAccount.metadata.privateAlias), + None, + None, + None, + None, + Some(otherAccount.metadata.addCorporateLocation), + Some(otherAccount.metadata.addPhysicalLocation), + Some(otherAccount.metadata.addPublicAlias), + Some(otherAccount.metadata.addPrivateAlias), + Some(otherAccount.metadata.deleteCorporateLocation), + Some(otherAccount.metadata.deletePhysicalLocation) + )) + + Some(new ModeratedOtherBankAccount(otherAccount.id,otherAccountLabel,None,None,None, + None, None, otherAccountMetadata, None)) + } +} + +object Owner extends FullView { + override def id = 8 + override def name="Owner" + override def permalink = "owner" +} + +object Management extends FullView { + override def id = 9 + override def name="Management" + override def permalink = "management" +} diff --git a/MavLift/src/main/scala/code/model/dataAccess/Account.scala b/MavLift/src/main/scala/code/model/dataAccess/Account.scala index 110dfde1a..b5714ba66 100644 --- a/MavLift/src/main/scala/code/model/dataAccess/Account.scala +++ b/MavLift/src/main/scala/code/model/dataAccess/Account.scala @@ -1,4 +1,4 @@ -/** +/** Open Bank Project - Transparency / Social Finance Web Application Copyright (C) 2011, 2012, TESOBE / Music Pictures Ltd @@ -15,21 +15,21 @@ 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 . -Email: contact@tesobe.com -TESOBE / Music Pictures Ltd +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd Osloerstrasse 16/17 Berlin 13359, Germany This product includes software developed at TESOBE (http://www.tesobe.com/) - by + 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.model.dataAccess import com.mongodb.QueryBuilder @@ -37,20 +37,20 @@ import net.liftweb.mongodb.JsonObjectMeta import net.liftweb.mongodb.JsonObject import net.liftweb.mongodb.record.MongoMetaRecord import net.liftweb.mongodb.record.field.ObjectIdPk +import net.liftweb.mongodb.record.field.ObjectIdRefListField import net.liftweb.mongodb.record.MongoRecord -import net.liftweb.mongodb.record.field.{BsonRecordField , ObjectIdRefField} +import net.liftweb.mongodb.record.field.ObjectIdRefField import net.liftweb.mongodb.record.field.MongoJsonObjectListField import net.liftweb.mongodb.record.field.DateField -import net.liftweb.common.{ Box, Empty, Full } -import net.liftweb.mongodb.record.field.BsonRecordListField +import net.liftweb.common.{ Box, Empty, Full, Failure } +import net.liftweb.mongodb.record.field.BsonRecordField import net.liftweb.mongodb.record.{ BsonRecord, BsonMetaRecord } -import net.liftweb.record.field.{ StringField, BooleanField } +import net.liftweb.record.field.{ StringField, BooleanField, DecimalField } import net.liftweb.mongodb.{Limit, Skip} -import code.model.dataAccess.OBPEnvelope._ -import code.model.traits.ModeratedTransaction -import code.model.traits.BankAccount -import code.model.implementedTraits.{ BankAccountImpl, AccountOwnerImpl } +import code.model.{ModeratedTransaction, AccountOwner, BankAccount} import net.liftweb.mongodb.BsonDSL._ +import java.util.Date +import OBPEnvelope._ /** @@ -63,6 +63,7 @@ import net.liftweb.mongodb.BsonDSL._ class Account extends MongoRecord[Account] with ObjectIdPk[Account] { def meta = Account + object balance extends DecimalField(this, 0) object anonAccess extends BooleanField(this, false) object holder extends StringField(this, 255) object number extends StringField(this, 255) @@ -74,36 +75,36 @@ class Account extends MongoRecord[Account] with ObjectIdPk[Account] { object currency extends StringField(this, 255) object iban extends StringField(this, 255) object lastUpdate extends DateField(this) - object otherAccounts extends BsonRecordListField(this, OtherAccount) - + object otherAccounts extends ObjectIdRefListField(this, OtherAccount) + def bankName : String = bankID.obj match { case Full(bank) => bank.name.get - case _ => "" + case _ => "" } def bankPermalink : String = bankID.obj match { case Full(bank) => bank.permalink.get - case _ => "" + case _ => "" } - + def transactionsForAccount = QueryBuilder.start("obp_transaction.this_account.number").is(number.get). put("obp_transaction.this_account.kind").is(kind.get). + put("obp_transaction.this_account.holder").is(holder.get). put("obp_transaction.this_account.bank.name").is(bankName) - //find all the envelopes related to this account + //find all the envelopes related to this account def allEnvelopes: List[OBPEnvelope] = OBPEnvelope.findAll(transactionsForAccount.get) def envelopes(queryParams: OBPQueryParam*): List[OBPEnvelope] = { val DefaultSortField = "obp_transaction.details.completed" - //This is ugly with the casts but it is a similar approach to mongo's .findAll implementation - val limit = queryParams.find(q => q.isInstanceOf[OBPLimit]).asInstanceOf[Option[OBPLimit]].map(x => x.value).getOrElse(50) - val offset = queryParams.find(q => q.isInstanceOf[OBPOffset]).asInstanceOf[Option[OBPOffset]].map(x => x.value).getOrElse(0) - val orderingParams = queryParams.find(q => q.isInstanceOf[OBPOrdering]). - asInstanceOf[Option[OBPOrdering]].map(x => x). - getOrElse(OBPOrdering(Some(DefaultSortField), OBPDescending)) - - val fromDate = queryParams.find(q => q.isInstanceOf[OBPFromDate]).asInstanceOf[Option[OBPFromDate]] - val toDate = queryParams.find(q => q.isInstanceOf[OBPToDate]).asInstanceOf[Option[OBPToDate]] - + + val limit = queryParams.collect { case OBPLimit(value) => value }.headOption.getOrElse(50) + val offset = queryParams.collect { case OBPOffset(value) => value }.headOption.getOrElse(0) + val orderingParams = queryParams.collect { case param: OBPOrdering => param}.headOption + .getOrElse(OBPOrdering(Some(DefaultSortField), OBPDescending)) + + val fromDate = queryParams.collect { case param: OBPFromDate => param }.headOption + val toDate = queryParams.collect { case param: OBPFromDate => param }.headOption + val mongoParams = { val start = transactionsForAccount val start2 = if(fromDate.isDefined) start.put("obp_transaction.details.completed").greaterThanEquals(fromDate.get.value) @@ -112,9 +113,9 @@ class Account extends MongoRecord[Account] with ObjectIdPk[Account] { else start2 end.get } - + val ordering = QueryBuilder.start(orderingParams.field.getOrElse(DefaultSortField)).is(orderingParams.order.orderValue).get - + OBPEnvelope.findAll(mongoParams, ordering, Limit(limit), Skip(offset)) } } @@ -122,20 +123,32 @@ class Account extends MongoRecord[Account] with ObjectIdPk[Account] { object Account extends Account with MongoMetaRecord[Account] { def toBankAccount(account: Account): BankAccount = { val iban = if (account.iban.toString.isEmpty) None else Some(account.iban.toString) - var bankAccount = new BankAccountImpl(account.id.toString, Set(), account.kind.toString, account.currency.toString, account.label.toString, - "", None, iban, account.anonAccess.get, account.number.get, account.bankName, account.bankPermalink, account.permalink.get) - val owners = Set(new AccountOwnerImpl("", account.holder.toString, Set(bankAccount))) - bankAccount.owners = Set(new AccountOwnerImpl("", account.holder.toString, Set(bankAccount))) - + val bankAccount = + new BankAccount( + account.id.toString, + Set(new AccountOwner("", account.holder.toString)), + account.kind.toString, + account.balance.get, + account.currency.toString, + account.name.get, + account.label.toString, + "", + None, + iban, + account.anonAccess.get, + account.number.get, + account.bankName, + account.bankPermalink, + account.permalink.get + ) bankAccount } } -class OtherAccount private () extends BsonRecord[OtherAccount] { +class OtherAccount private() extends MongoRecord[OtherAccount] with ObjectIdPk[OtherAccount] { def meta = OtherAccount object holder extends StringField(this, 200) - object publicAlias extends StringField(this, 100) object privateAlias extends StringField(this, 100) object moreInfo extends StringField(this, 100) @@ -144,26 +157,66 @@ class OtherAccount private () extends BsonRecord[OtherAccount] { object openCorporatesUrl extends StringField(this, 100) { override def optional_? = true } + object corporateLocation extends BsonRecordField(this, OBPGeoTag) + object physicalLocation extends BsonRecordField(this, OBPGeoTag) + + def addCorporateLocation(userId: String, viewId : Long, datePosted : Date, longitude : Double, latitude : Double) : Boolean = { + val newTag = OBPGeoTag.createRecord. + userId(userId). + viewID(viewId). + date(datePosted). + geoLongitude(longitude). + geoLatitude(latitude) + corporateLocation(newTag).save + true + } + + def deleteCorporateLocation : Boolean = { + corporateLocation.clear + this.save + true + } + + def addPhysicalLocation(userId: String, viewId : Long, datePosted : Date, longitude : Double, latitude : Double) : Boolean = { + val newTag = OBPGeoTag.createRecord. + userId(userId). + viewID(viewId). + date(datePosted). + geoLongitude(longitude). + geoLatitude(latitude) + physicalLocation(newTag).save + true + } + + def deletePhysicalLocation : Boolean = { + physicalLocation.clear + this.save + true + } + } -object OtherAccount extends OtherAccount with BsonMetaRecord[OtherAccount] +object OtherAccount extends OtherAccount with MongoMetaRecord[OtherAccount] class HostedBank extends MongoRecord[HostedBank] with ObjectIdPk[HostedBank]{ def meta = HostedBank object name extends StringField(this, 255) object alias extends StringField(this, 255) - object logo extends StringField(this, 255) + object logoURL extends StringField(this, 255) object website extends StringField(this, 255) object email extends StringField(this, 255) object permalink extends StringField(this, 255) object SWIFT_BIC extends StringField(this, 255) object national_identifier extends StringField(this, 255) - def getAccount(bankAccountPermalink : String) : Box[Account] = - Account.find(("permalink" -> bankAccountPermalink) ~ ("bankID" -> id.is)) - - def isAccount(bankAccountPermalink : String) : Boolean = + def getAccount(bankAccountPermalink : String) : Box[Account] = + Account.find(("permalink" -> bankAccountPermalink) ~ ("bankID" -> id.is)) match { + case Full(account) => Full(account) + case _ => Failure("account " + bankAccountPermalink +" not found in bank " + permalink, Empty, Empty) + } + + def isAccount(bankAccountPermalink : String) : Boolean = Account.count(("permalink" -> bankAccountPermalink) ~ ("bankID" -> id.is)) == 1 } diff --git a/MavLift/src/main/scala/code/model/dataAccess/Admin.scala b/MavLift/src/main/scala/code/model/dataAccess/Admin.scala new file mode 100644 index 000000000..4204839de --- /dev/null +++ b/MavLift/src/main/scala/code/model/dataAccess/Admin.scala @@ -0,0 +1,108 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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.model.dataAccess + +import net.liftweb.mapper._ +import net.liftweb.util._ +import net.liftweb.common._ +import scala.xml.NodeSeq +import net.liftweb.sitemap.Loc.LocGroup +import net.liftweb.http.{S,SessionVar,Templates} +import net.liftweb.json.JsonDSL._ +import net.liftweb.http.{SHtml,S} +import net.liftweb.util.Helpers._ +import org.bson.types.ObjectId +import com.mongodb.DBObject +import net.liftweb.json.JsonAST.JObject + + +/** + * This class to handel the administration of the API, like the API OAuth keys. + */ +class Admin extends MegaProtoUser[Admin] { + def getSingleton = Admin // what's the "meta" server +} + +object Admin extends Admin with MetaMegaProtoUser[Admin]{ + + override def dbTableName = "admins" // define the DB table name + + override def screenWrap = Full( + ) + // define the order fields will appear in forms and output + override def fieldOrder = List(id, firstName, lastName, email, + locale, timezone, password) + + // comment this line out to require email validations + override def skipEmailValidation = true + + //Keep track of the referer on login + object loginReferer extends SessionVar("/") + + //This is where the user gets redirected to after login + override def homePage = { + val ret = loginReferer.is + loginReferer.remove() + ret + } + + override def loginXhtml = { + import net.liftweb.http.TemplateFinder + import net.liftweb.http.js.JsCmds.Noop + val loginXml = Templates(List("templates-hidden","_Adminlogin")).map({ + "form [action]" #> {S.uri} & + "#loginText * " #> {S.??("log.in")} & + "#emailAddressText * " #> {S.??("email.address")} & + "#passwordText * " #> {S.??("password")} & + "#recoverPasswordLink * " #> { + "a [href]" #> {lostPasswordPath.mkString("/", "/", "")} & + "a *" #> {S.??("recover.password")} + } + }) + SHtml.span(loginXml getOrElse NodeSeq.Empty,Noop) + } + + //disable the sign up page + override def createUserMenuLoc = Empty + + // the admin edit page + override def editUserMenuLoc = Empty + + //Set the login referer + override def login = { + for( + r <- S.referer + if loginReferer.is.equals("/") + ) loginReferer.set(r) + super.login + } +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/dataAccess/Connectors.scala b/MavLift/src/main/scala/code/model/dataAccess/Connectors.scala index b6169a793..2d093c1d7 100644 --- a/MavLift/src/main/scala/code/model/dataAccess/Connectors.scala +++ b/MavLift/src/main/scala/code/model/dataAccess/Connectors.scala @@ -1,4 +1,4 @@ -/** +/** Open Bank Project - Transparency / Social Finance Web Application Copyright (C) 2011, 2012, TESOBE / Music Pictures Ltd @@ -15,14 +15,14 @@ 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 . -Email: contact@tesobe.com -TESOBE / Music Pictures Ltd +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd Osloerstrasse 16/17 Berlin 13359, Germany This product includes software developed at TESOBE (http://www.tesobe.com/) - by + by Simon Redfern : simon AT tesobe DOT com Stefan Bethge : stefan AT tesobe DOT com Everett Sochowski : everett AT tesobe DOT com @@ -31,59 +31,77 @@ Berlin 13359, Germany */ package code.model.dataAccess -import code.model.traits._ -import code.model.implementedTraits._ -import net.liftweb.common.{ Box, Empty, Full } +import code.model._ +import net.liftweb.common.{ Box, Empty, Full, Failure } +import net.liftweb.util.Helpers.tryo import net.liftweb.mongodb.BsonDSL._ import net.liftweb.json.JsonDSL._ import net.liftweb.common.Loggable import code.model.dataAccess.OBPEnvelope.OBPQueryParam -import net.liftweb.mapper.By +import net.liftweb.mapper.{By,IHaveValidatedThisSQL} import net.liftweb.mongodb.MongoDB import com.mongodb.BasicDBList import java.util.ArrayList import org.bson.types.ObjectId +import net.liftweb.mapper.BySql +import net.liftweb.db.DB object LocalStorage extends MongoDBLocalStorage trait LocalStorage extends Loggable { - def getModeratedTransactions(permalink: String, bankPermalink: String) - (moderate: Transaction => ModeratedTransaction): List[ModeratedTransaction] = { - val rawTransactions = getTransactions(permalink, bankPermalink) getOrElse Nil - rawTransactions.map(moderate) - } - - def getModeratedTransactions(permalink: String, bankPermalink: String, queryParams: OBPQueryParam*) - (moderate: Transaction => ModeratedTransaction): List[ModeratedTransaction] = { - val rawTransactions = getTransactions(permalink, bankPermalink, queryParams: _*) getOrElse Nil - rawTransactions.map(moderate) - } - - def getTransactions(permalink: String, bankPermalink: String, queryParams: OBPQueryParam*) : Box[List[Transaction]] = { - val envelopesForAccount = (acc: Account) => acc.envelopes(queryParams: _*) - getTransactions(permalink, bankPermalink, envelopesForAccount) - } - - def getTransactions(permalink: String, bankPermalink: String): Box[List[Transaction]] = { - val envelopesForAccount = (acc: Account) => acc.allEnvelopes - getTransactions(permalink, bankPermalink, envelopesForAccount) - } - - def getTransactions(bank: String, account: String, envelopesForAccount: Account => List[OBPEnvelope]): Box[List[Transaction]] def getBank(name: String): Box[Bank] - def getBankAccounts(bank: Bank): Set[BankAccount] - + def allBanks : List[Bank] + + //TODO: remove after the split because useless + def getAccount(bankpermalink: String, account: String): Box[Account] + + def getBankAccount(bankId : String, bankAccountId : String) : Box[BankAccount] + + def getAllPublicAccounts() : List[BankAccount] + + def getPublicBankAccounts(bank : Bank) : Box[List[BankAccount]] + + def getNonPublicBankAccounts(user : User) : Box[List[BankAccount]] + + def getNonPublicBankAccounts(user : User, bankID : String) : Box[List[BankAccount]] + + //TODO: remove after the split because useless def correctBankAndAccount(bank: String, account: String): Boolean - def getAccount(bankpermalink: String, account: String): Box[Account] - def getTransaction(id : String, bankPermalink : String, accountPermalink : String) : Box[Transaction] + def getModeratedOtherBankAccount(accountID : String, otherAccountID : String) + (moderate: OtherBankAccount => Option[ModeratedOtherBankAccount]) : Box[ModeratedOtherBankAccount] + + def getModeratedOtherBankAccounts(accountID : String) + (moderate: OtherBankAccount => Option[ModeratedOtherBankAccount]): Box[List[ModeratedOtherBankAccount]] + + def getModeratedTransactions(permalink: String, bankPermalink: String, queryParams: OBPQueryParam*) + (moderate: Transaction => ModeratedTransaction): Box[List[ModeratedTransaction]] + + def getUser(id : String) : Box[User] + + def getCurrentUser : Box[User] + + def permissions(account : BankAccount) : Box[List[Permission]] + + def addPermission(bankAccountId : String, view : View, user : User) : Box[Boolean] + + def addPermissions(bankAccountId : String, views : List[View], user : User) : Box[Boolean] + + def revokePermission(bankAccountId : String, view : View, user : User) : Box[Boolean] + + def revokeAllPermission(bankAccountId : String, user : User) : Box[Boolean] + + def views(bankAccountID : String) : Box[List[View]] + } class MongoDBLocalStorage extends LocalStorage { - private def createTransaction(env: OBPEnvelope, theAccount: Account): Transaction = - { + + private val availableViews = List(Team, Board, Authorities, Public, OurNetwork, Owner, Management) + + private def createTransaction(env: OBPEnvelope, theAccount: Account): Transaction = { import net.liftweb.json.JsonDSL._ val transaction: OBPTransaction = env.obp_transaction.get val thisAccount = transaction.this_account @@ -91,94 +109,563 @@ class MongoDBLocalStorage extends LocalStorage { val otherUnmediatedHolder = otherAccount_.holder.get val thisBankAccount = Account.toBankAccount(theAccount) - - val oAccs = theAccount.otherAccounts.get - val oAccOpt = oAccs.find(o => { - otherUnmediatedHolder.equals(o.holder.get) - }) - val oAcc = oAccOpt getOrElse { + val oAcc = theAccount.otherAccounts.objs.find(o => { + otherUnmediatedHolder.equals(o.holder.get) + }).getOrElse { OtherAccount.createRecord } val id = env.id.is.toString() - val oSwiftBic = None - val otherAccount = new OtherBankAccountImpl( - id_ = "", - label_ = otherAccount_.holder.get, - nationalIdentifier_ = otherAccount_.bank.get.national_identifier.get, - swift_bic_ = None, //TODO: need to add this to the json/model - iban_ = Some(otherAccount_.bank.get.IBAN.get), - number_ = otherAccount_.number.get, - bankName_ = "", //TODO: need to add this to the json/model - metadata_ = new OtherBankAccountMetadataImpl(oAcc.publicAlias.get, oAcc.privateAlias.get, oAcc.moreInfo.get, - oAcc.url.get, oAcc.imageUrl.get, oAcc.openCorporatesUrl.get)) - val metadata = new TransactionMetadataImpl(env.narrative.get, env.obp_comments.objs, - (text => env.narrative(text).save), env.addComment _) - val transactionType = env.obp_transaction.get.details.get.type_en.get - val amount = env.obp_transaction.get.details.get.value.get.amount.get - val currency = env.obp_transaction.get.details.get.value.get.currency.get - val label = None - val startDate = env.obp_transaction.get.details.get.posted.get - val finishDate = env.obp_transaction.get.details.get.completed.get - val balance = env.obp_transaction.get.details.get.new_balance.get.amount.get - new TransactionImpl(id, thisBankAccount, otherAccount, metadata, transactionType, amount, currency, - label, startDate, finishDate, balance) - } - - def getTransactions(permalink: String, bankPermalink: String, envelopesForAccount: Account => List[OBPEnvelope]): Box[List[Transaction]] = - { - logger.debug("getTransactions for " + bankPermalink + "/" + permalink) - HostedBank.find("permalink",bankPermalink) match { - case Full (bank) => bank.getAccount(permalink) match { - case Full(account) => { - val envs = envelopesForAccount(account) - Full(envs.map(createTransaction(_, account))) - } - case _ => Empty - } - case _ => Empty - } + val uuid = id + val otherAccountMetadata = + new OtherBankAccountMetadata( + publicAlias = oAcc.publicAlias.get, + privateAlias = oAcc.privateAlias.get, + moreInfo = oAcc.moreInfo.get, + url = oAcc.url.get, + imageURL = oAcc.imageUrl.get, + openCorporatesURL = oAcc.openCorporatesUrl.get, + corporateLocation = oAcc.corporateLocation.get, + physicalLocation = oAcc.physicalLocation.get, + addMoreInfo = (text => { + oAcc.moreInfo(text).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addURL = (text => { + oAcc.url(text).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addImageURL = (text => { + oAcc.imageUrl(text).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addOpenCorporatesURL = (text => { + oAcc.openCorporatesUrl(text).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addCorporateLocation = oAcc.addCorporateLocation, + addPhysicalLocation = oAcc.addPhysicalLocation, + addPublicAlias = (alias => { + oAcc.publicAlias(alias).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addPrivateAlias = (alias => { + oAcc.privateAlias(alias).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + deleteCorporateLocation = oAcc.deleteCorporateLocation _, + deletePhysicalLocation = oAcc.deletePhysicalLocation _ + ) + val otherAccount = new OtherBankAccount( + id = oAcc.id.is.toString, + label = otherAccount_.holder.get, + nationalIdentifier = otherAccount_.bank.get.national_identifier.get, + swift_bic = None, //TODO: need to add this to the json/model + iban = Some(otherAccount_.bank.get.IBAN.get), + number = otherAccount_.number.get, + bankName = otherAccount_.bank.get.name.get, + metadata = otherAccountMetadata, + kind = "" + ) + val metadata = new TransactionMetadata( + env.narrative.get, + (text => env.narrative(text).save), + env.obp_comments.objs, + env.addComment, + env.deleteComment, + env.tags.objs, + env.addTag, + env.deleteTag, + env.images.objs, + env.addImage, + env.deleteImage, + env.whereTags.get, + env.addWhereTag, + env.deleteWhereTag + ) + val transactionType = transaction.details.get.type_en.get + val amount = transaction.details.get.value.get.amount.get + val currency = transaction.details.get.value.get.currency.get + val label = Some(transaction.details.get.label.get) + val startDate = transaction.details.get.posted.get + val finishDate = transaction.details.get.completed.get + val balance = transaction.details.get.new_balance.get.amount.get + new Transaction( + uuid, + id, + thisBankAccount, + otherAccount, + metadata, + transactionType, + amount, + currency, + label, + startDate, + finishDate, + balance + ) } - def getBank(permalink: String): Box[Bank] = - HostedBank.find("permalink", permalink). - map( bank => new BankImpl(bank.id.toString, bank.name.get, permalink)) - - - def allBanks : List[Bank] = - HostedBank.findAll. - map(bank => new BankImpl(bank.id.toString, bank.name.get, bank.permalink.get)) - - def getBankAccounts(bank: Bank): Set[BankAccount] = { - val bankId = new ObjectId(bank.id) - val rawAccounts = Account.findAll(("bankID" -> bankId)).toSet - rawAccounts.map(Account.toBankAccount) + private def createOtherBankAccount(otherAccount : OtherAccount, otherAccountFromTransaction : OBPAccount) : OtherBankAccount = { + val metadata = + new OtherBankAccountMetadata( + publicAlias = otherAccount.publicAlias.get, + privateAlias = otherAccount.privateAlias.get, + moreInfo = otherAccount.moreInfo.get, + url = otherAccount.url.get, + imageURL = otherAccount.imageUrl.get, + openCorporatesURL = otherAccount.openCorporatesUrl.get, + corporateLocation = otherAccount.corporateLocation.get, + physicalLocation = otherAccount.physicalLocation.get, + addMoreInfo = (text => { + otherAccount.moreInfo(text).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addURL = (text => { + otherAccount.url(text).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addImageURL = (text => { + otherAccount.imageUrl(text).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addOpenCorporatesURL = (text => { + otherAccount.openCorporatesUrl(text).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addCorporateLocation = otherAccount.addCorporateLocation, + addPhysicalLocation = otherAccount.addPhysicalLocation, + addPublicAlias = (alias => { + otherAccount.publicAlias(alias).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + addPrivateAlias = (alias => { + otherAccount.privateAlias(alias).save + //the save method does not return a Boolean to inform about the saving state, + //so we a true + true + }), + deleteCorporateLocation = otherAccount.deleteCorporateLocation _, + deletePhysicalLocation = otherAccount.deletePhysicalLocation _ + ) + + new OtherBankAccount( + id = otherAccount.id.is.toString, + label = otherAccount.holder.get, + nationalIdentifier = otherAccountFromTransaction.bank.get.national_identifier.get, + swift_bic = None, //TODO: need to add this to the json/model + iban = Some(otherAccountFromTransaction.bank.get.IBAN.get), + number = otherAccountFromTransaction.number.get, + bankName = otherAccountFromTransaction.bank.get.name.get, + metadata = metadata, + kind = "" + ) } - - //check if the bank and the accounts exist in the database - def correctBankAndAccount(bank: String, account: String): Boolean = - HostedBank.find("permalink",bank) match { - case Full(bank) => bank.isAccount(account) - case _ => false - } - def getAccount(bankpermalink: String, account: String): Box[Account] = - HostedBank.find("permalink",bankpermalink) match { - case Full (bank) => bank.getAccount(account) - case _ => Empty + + private def setPrivilegeFromView(privilege : Privilege, view : View, value : Boolean ) = { + view match { + case OurNetwork => privilege.ourNetworkPermission(value) + case Team => privilege.teamPermission(value) + case Board => privilege.boardPermission(value) + case Authorities => privilege.authoritiesPermission(value) + case Owner => privilege.ownerPermission(value) + case Management => privilege.mangementPermission(value) + case _ => } - - def getTransaction(id : String, bankPermalink : String, accountPermalink : String) : Box[Transaction] = - { + } + + private def createBank(bank : HostedBank) : Bank = { + new Bank( + bank.id.is.toString, + bank.alias.is, + bank.name.is, + bank.permalink.is, + bank.logoURL.is, + bank.website.is + ) + } + + private def getHostedBank(permalink : String) : Box[HostedBank] = { for{ - bank <- HostedBank.find("permalink",bankPermalink) + bank <- HostedBank.find("permalink", permalink) ?~ {"bank " + permalink + " not found"} + } yield bank + } + + private def getTransaction(id : String, bankPermalink : String, accountPermalink : String) : Box[Transaction] = { + for{ + bank <- getHostedBank(bankPermalink) account <- bank.getAccount(accountPermalink) - ifTransactionsIsInAccount <- Full(account.transactionsForAccount.put("_id").is(new ObjectId(id)).get) + objectId <- tryo{new ObjectId(id)} ?~ {"Transaction "+id+" not found"} + ifTransactionsIsInAccount <- Full(account.transactionsForAccount.put("_id").is(objectId).get) envelope <- OBPEnvelope.find(ifTransactionsIsInAccount) } yield createTransaction(envelope,account) } - def getAllAccounts() : List[Account] = Account.findAll + private def getTransactions(permalink: String, bankPermalink: String, queryParams: OBPQueryParam*): Box[List[Transaction]] = { + logger.debug("getTransactions for " + bankPermalink + "/" + permalink) + for{ + bank <- getHostedBank(bankPermalink) + account <- bank.getAccount(permalink) + } yield account.envelopes(queryParams: _*).map(createTransaction(_, account)) + } + + def getBank(permalink: String): Box[Bank] = + for{ + bank <- getHostedBank(permalink) + } yield createBank(bank) + + def allBanks : List[Bank] = + HostedBank.findAll.map(createBank) + + //TODO: remove after the split because useless + def getAccount(bankpermalink: String, account: String): Box[Account] = + for{ + hostedBank <- getHostedBank(bankpermalink) + account <- hostedBank.getAccount(account) + } yield account + + def getBankAccount(bankId : String, bankAccountId : String) : Box[BankAccount] = { + for{ + bank <- getHostedBank(bankId) + account <- bank.getAccount(bankAccountId) + } yield Account toBankAccount account + } + + def getAllPublicAccounts() : List[BankAccount] = Account.findAll("anonAccess", true) map Account.toBankAccount + + def getPublicBankAccounts(bank : Bank) : Box[List[BankAccount]] = { + for{ + id <- tryo{new ObjectId(bank.id)} ?~ {"bank " + bank.fullName + " not found"} + } yield Account.findAll(("bankID",id) ~ ("anonAccess", true)).map(Account.toBankAccount) + } - def getAllPublicAccounts() : List[Account] = Account.findAll("anonAccess", true) + private def moreThanAnonHostedAccounts(user : User) : Box[List[HostedAccount]] = { + user match { + case u : OBPUser => { + val hostedAccountTable = HostedAccount._dbTableNameLC + val privilegeTable = Privilege._dbTableNameLC + val userTable = OBPUser._dbTableNameLC + + val hostedId = hostedAccountTable + "." + HostedAccount.id.dbColumnName + val hostedAccId = hostedAccountTable + "." + HostedAccount.accountID.dbColumnName + val privilegeAccId = privilegeTable + "." + Privilege.account.dbColumnName + val privilegeUserId = privilegeTable + "." + Privilege.user.dbColumnName + + val ourNetworkPrivilege = privilegeTable + "." + Privilege.ourNetworkPermission.dbColumnName + val teamPrivilege = privilegeTable + "." + Privilege.teamPermission.dbColumnName + val boardPrivilege = privilegeTable + "." + Privilege.boardPermission.dbColumnName + val authoritiesPrivilege = privilegeTable + "." + Privilege.authoritiesPermission.dbColumnName + val ownerPrivilege = privilegeTable + "." + Privilege.ownerPermission.dbColumnName + val managementPrivilege = privilegeTable + "." + Privilege.mangementPermission.dbColumnName + + val query = "SELECT DISTINCT " + hostedId + ", " + hostedAccId + + " FROM " + hostedAccountTable + ", " + privilegeTable + ", " + userTable + + " WHERE " + "( " + hostedId + " = " + privilegeAccId + ")" + + " AND " + "( " + privilegeUserId + " = ? "/* + u.id.get*/ + ")"+ + " AND " + "( " + ourNetworkPrivilege + " = true" + + " OR " + teamPrivilege + " = true" + + " OR " + boardPrivilege + " = true" + + " OR " + authoritiesPrivilege + " = true" + + " OR " + managementPrivilege + " = true" + + " OR " + ownerPrivilege + " = true)" + + Full(HostedAccount.findAllByPreparedStatement({ + superconn => { + val statement = superconn.connection.prepareStatement(query) + statement.setLong(1, u.id.get) + statement + } + })) + } + case _ => { + logger.error("OBPUser instance not found, could not execute the SQL query ") + Failure("could not find non public bank accounts") + + } + + } + } + + /** + * @return the bank accounts where the user has at least access to a non public view (is_public==false) + */ + def getNonPublicBankAccounts(user : User) : Box[List[BankAccount]] = { + + user match { + case u : OBPUser => { + + for { + moreThanAnon <- moreThanAnonHostedAccounts(u) + } yield { + val mongoIds = moreThanAnon.map(hAcc => new ObjectId(hAcc.accountID.get)) + Account.findAll(mongoIds).map(Account.toBankAccount) + } + + } + case u: User => { + logger.error("OBPUser instance not found, could not execute the SQL query ") + Failure("could not find non public bank accounts") + } + } + } + + /** + * @return the bank accounts where the user has at least access to a non public view (is_public==false) for a specific bank + */ + def getNonPublicBankAccounts(user : User, bankID : String) : Box[List[BankAccount]] = { + user match { + case u : OBPUser => { + + for { + moreThanAnon <- moreThanAnonHostedAccounts(u) + bankObjectId <- tryo{new ObjectId(bankID)} + } yield { + + def sameBank(account : Account) : Boolean = + account.bankID.get == bankObjectId + + val mongoIds = moreThanAnon.map(hAcc => new ObjectId(hAcc.accountID.get)) + Account.findAll(mongoIds).filter(sameBank).map(Account.toBankAccount) + } + + } + case u : User => { + logger.error("OBPUser instance not found, could not execute the SQL query ") + Failure("could not find non public bank accounts") + } + } + } + + //TODO: remove after the split because useless + def correctBankAndAccount(bank: String, account: String): Boolean = + getHostedBank(bank) match { + case Full(bank) => bank.isAccount(account) + case _ => false + } + + def getModeratedOtherBankAccount(accountID : String, otherAccountID : String) + (moderate: OtherBankAccount => Option[ModeratedOtherBankAccount]): Box[ModeratedOtherBankAccount] = { + for{ + id <- tryo{new ObjectId(accountID)} ?~ {"account " + accountID + " not found"} + account <- Account.find("_id",id) + otherAccount <- account.otherAccounts.objs.find(_.id.get.equals(otherAccountID)) + } yield{ + val otherAccountFromTransaction : OBPAccount = OBPEnvelope.find("obp_transaction.other_account.holder",otherAccount.holder.get) match { + case Full(envelope) => + envelope.obp_transaction.get.other_account.get + case _ => OBPAccount.createRecord + } + moderate(createOtherBankAccount(otherAccount, otherAccountFromTransaction)).get + } + } + + def getModeratedOtherBankAccounts(accountID : String) + (moderate: OtherBankAccount => Option[ModeratedOtherBankAccount]): Box[List[ModeratedOtherBankAccount]] = { + for{ + id <- tryo{new ObjectId(accountID)} ?~ {"account " + accountID + " not found"} + account <- Account.find("_id",id) + } yield{ + val otherBankAccounts = account.otherAccounts.objs.map(otherAccount => { + //for legacy reasons some of the data about the "other account" are stored only on the transactions + //so we need first to get a transaction that match to have the rest of the data + val otherAccountFromTransaction : OBPAccount = OBPEnvelope.find("obp_transaction.other_account.holder",otherAccount.holder.get) match { + case Full(envelope) => + envelope.obp_transaction.get.other_account.get + case _ => OBPAccount.createRecord + } + createOtherBankAccount(otherAccount, otherAccountFromTransaction) + }) + + (otherBankAccounts.map(moderate)).collect{case Some(t) => t} + } + } + + def getModeratedTransactions(permalink: String, bankPermalink: String, queryParams: OBPQueryParam*) + (moderate: Transaction => ModeratedTransaction): Box[List[ModeratedTransaction]] = { + for{ + rawTransactions <- getTransactions(permalink, bankPermalink, queryParams: _*) + } yield rawTransactions.map(moderate) + } + + def getUser(id : String) : Box[User] = + OBPUser.find(By(OBPUser.email,id)) match { + case Full(u) => Full(u) + case _ => Failure("user " + id + " not found") + } + + def getModeratedTransaction(id : String, bankPermalink : String, accountPermalink : String) + (moderate: Transaction => ModeratedTransaction) : Box[ModeratedTransaction] = { + for{ + transaction <- getTransaction(id,bankPermalink,accountPermalink) + } yield moderate(transaction) + } + + def getCurrentUser : Box[User] = OBPUser.currentUser + + def permissions(account : BankAccount) : Box[List[Permission]] = { + + HostedAccount.find(By(HostedAccount.accountID,account.id)) match { + case Full(acc) => { + val privileges = Privilege.findAll(By(Privilege.account, acc.id.get)).sortWith((p1,p2) => p1.updatedAt.get after p2.updatedAt.get) + val permissions : List[Box[Permission]] = privileges.map( p => { + if( + p.ourNetworkPermission.get != false + | p.teamPermission.get != false + | p.boardPermission.get != false + | p.authoritiesPermission.get != false + | p.ownerPermission.get != false + | p.mangementPermission.get != false + ) + p.user.obj.map(u => { + new Permission( + u, + u.permittedViews(account).toList + ) + }) + else + Empty + }) + Full(permissions.flatten) + } + case _ => Failure("Could not find the hostedAccount", Empty, Empty) + } + } + + def addPermission(bankAccountId : String, view : View, user : User) : Box[Boolean] = { + user match { + case u: OBPUser => + for{ + bankAccount <- HostedAccount.find(By(HostedAccount.accountID, bankAccountId)) + } yield { + Privilege.find(By(Privilege.user, u.id), By(Privilege.account, bankAccount)) match { + //update the existing privilege + case Full(privilege) => { + setPrivilegeFromView(privilege, view, true) + privilege.save + } + //there is no privilege to this user, so we create one + case _ => { + val privilege = + Privilege.create. + user(u.id). + account(bankAccount) + setPrivilegeFromView(privilege, view, true) + privilege.save + } + } + } + case u: User => { + logger.error("OBPUser instance not found, could not grant access ") + Empty + } + } + } + + def addPermissions(bankAccountId : String, views : List[View], user : User) : Box[Boolean] ={ + user match { + case u : OBPUser => { + for{ + bankAccount <- HostedAccount.find(By(HostedAccount.accountID, bankAccountId)) + } yield { + Privilege.find(By(Privilege.user, u.id), By(Privilege.account, bankAccount)) match { + //update the existing privilege + case Full(privilege) => { + views.map(v => { + setPrivilegeFromView(privilege, v, true) + }) + privilege.save + } + //there is no privilege to this user, so we create one + case _ => { + val privilege = + Privilege.create. + user(u.id). + account(bankAccount) + views.map(v => { + setPrivilegeFromView(privilege, v, true) + }) + privilege.save + } + } + } + } + case u: User => { + logger.error("OBPUser instance not found, could not grant access ") + Empty + } + } + + } + def revokePermission(bankAccountId : String, view : View, user : User) : Box[Boolean] = { + user match { + case user:OBPUser => + for{ + bankAccount <- HostedAccount.find(By(HostedAccount.accountID, bankAccountId)) + } yield { + Privilege.find(By(Privilege.user, user.id), By(Privilege.account, bankAccount)) match { + case Full(privilege) => { + setPrivilegeFromView(privilege, view, false) + privilege.save + } + //there is no privilege to this user, so there is nothing to revoke + case _ => true + } + } + case u: User => { + logger.error("OBPUser instance not found, could not revoke access ") + Empty + } + } + } + + def revokeAllPermission(bankAccountId : String, user : User) : Box[Boolean] = { + user match { + case user:OBPUser => + for{ + bankAccount <- HostedAccount.find(By(HostedAccount.accountID, bankAccountId)) + } yield { + Privilege.find(By(Privilege.user, user.id), By(Privilege.account, bankAccount)) match { + case Full(privilege) => { + availableViews.foreach({view => + setPrivilegeFromView(privilege, view, false) + }) + privilege.save + } + //there is no privilege to this user, so there is nothing to revoke + case _ => true + } + } + case u: User => { + logger.error("OBPUser instance not found, could not revoke access ") + Empty + } + } + } + + def views(bankAccountID : String) : Box[List[View]] = { + Full(availableViews) + } } \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/dataAccess/MongoConfig.scala b/MavLift/src/main/scala/code/model/dataAccess/MongoConfig.scala index 7d699668d..63f60cc1c 100644 --- a/MavLift/src/main/scala/code/model/dataAccess/MongoConfig.scala +++ b/MavLift/src/main/scala/code/model/dataAccess/MongoConfig.scala @@ -1,4 +1,4 @@ -/** +/** Open Bank Project - Transparency / Social Finance Web Application Copyright (C) 2011, 2012, TESOBE / Music Pictures Ltd @@ -15,14 +15,14 @@ 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 . -Email: contact@tesobe.com -TESOBE / Music Pictures Ltd +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd Osloerstrasse 16/17 Berlin 13359, Germany This product includes software developed at TESOBE (http://www.tesobe.com/) - by + by Simon Redfern : simon AT tesobe DOT com Stefan Bethge : stefan AT tesobe DOT com Everett Sochowski : everett AT tesobe DOT com @@ -43,10 +43,16 @@ object AdminDb extends MongoIdentifier { object MongoConfig { def init: Unit = { val srvr = new ServerAddress( - Props.get("mongo.host", "obp_mongod"), + Props.get("mongo.host", "localhost"), Props.getInt("mongo.port", 27017) ) - MongoDB.defineDb(DefaultMongoIdentifier, new Mongo(srvr), "OBP006") + val defaultDatabase = + Props.mode match { + case Props.RunModes.Test => "test" + case _ => "OBP006" + } + + MongoDB.defineDb(DefaultMongoIdentifier, new Mongo(srvr), Props.get("mongo.dbName", defaultDatabase)) MongoDB.defineDb(AdminDb, new Mongo(srvr), "admin") } } diff --git a/MavLift/src/main/scala/code/model/dataAccess/OBPTransaction.scala b/MavLift/src/main/scala/code/model/dataAccess/OBPTransaction.scala index 63b3d9ad2..e4f23ad9a 100644 --- a/MavLift/src/main/scala/code/model/dataAccess/OBPTransaction.scala +++ b/MavLift/src/main/scala/code/model/dataAccess/OBPTransaction.scala @@ -1,4 +1,4 @@ -/** +/** Open Bank Project - Transparency / Social Finance Web Application Copyright (C) 2011, 2012, TESOBE / Music Pictures Ltd @@ -15,14 +15,14 @@ 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 . -Email: contact@tesobe.com -TESOBE / Music Pictures Ltd +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd Osloerstrasse 16/17 Berlin 13359, Germany This product includes software developed at TESOBE (http://www.tesobe.com/) - by + by Simon Redfern : simon AT tesobe DOT com Stefan Bethge : stefan AT tesobe DOT com Everett Sochowski : everett AT tesobe DOT com @@ -47,8 +47,13 @@ import net.liftweb.mongodb.record.field.{MongoJsonObjectListField, MongoRefField import scala.util.Random import com.mongodb.QueryBuilder import com.mongodb.BasicDBObject -import code.model.traits.Comment +import code.model.{Comment,Tag,GeoTag,TransactionImage, User} import net.liftweb.common.Loggable +import org.bson.types.ObjectId +import net.liftweb.util.Helpers._ +import net.liftweb.http.S +import java.net.URL +import net.liftweb.record.field.{DoubleField,DecimalField} /** * "Current Account View" @@ -141,38 +146,25 @@ curl -i -H "Content-Type: application/json" -X POST -d '[{ */ // Seems to map to a collection of the plural name -class OBPEnvelope private() extends MongoRecord[OBPEnvelope] with ObjectIdPk[OBPEnvelope] { +class OBPEnvelope private() extends MongoRecord[OBPEnvelope] with ObjectIdPk[OBPEnvelope] with Loggable{ def meta = OBPEnvelope - /** - * Add a user generated comment to the transaction. Saves the db model when called. - * - * @param email The email address of the person posting the comment - * @param text The text of the comment - */ - def addComment(userId: Long, viewId : Long, text: String, datePosted : Date) = { - val comment = OBPComment.createRecord.userId(userId). - textField(text). - date(datePosted). - viewID(viewId).save - obp_comments(comment.id.is :: obp_comments.get ).save - } + // This creates a json attribute called "obp_transaction" + object obp_transaction extends BsonRecordField(this, OBPTransaction) + + object narrative extends StringField(this, 255) + + //not named comments as "comments" was used in an older mongo document version + object obp_comments extends ObjectIdRefListField[OBPEnvelope, OBPComment](this, OBPComment) + + object tags extends ObjectIdRefListField(this, OBPTag) + + object images extends ObjectIdRefListField(this, OBPTransactionImage) + + //we store a list of geo tags, one per view + object whereTags extends BsonRecordListField(this, OBPGeoTag) + - lazy val theAccount = { - val thisAcc = obp_transaction.get.this_account.get - val num = thisAcc.number.get - val accKind = thisAcc.kind.get - val bankName = thisAcc.bank.get.name.get - val accQry = QueryBuilder.start("number").is(num). - put("kind").is(accKind).get - - for { - account <- Account.find(accQry) - bank <- HostedBank.find("name", bankName) - if(bank.id.get == account.bankID.get) - } yield account - } - object DateDescending extends Ordering[OBPEnvelope] { def compare(e1: OBPEnvelope, e2: OBPEnvelope) = { val date1 = e1.obp_transaction.get.details.get.completed.get @@ -180,20 +172,205 @@ class OBPEnvelope private() extends MongoRecord[OBPEnvelope] with ObjectIdPk[OBP date1.compareTo(date2) } } - + + lazy val theAccount = { + val thisAcc = obp_transaction.get.this_account.get + val num = thisAcc.number.get + val accKind = thisAcc.kind.get + val bankName = thisAcc.bank.get.name.get + val holder = thisAcc.holder.get + val accQry = QueryBuilder.start("number").is(num). + put("kind").is(accKind).put("holder").is(holder).get + + for { + account <- Account.find(accQry) + bank <- HostedBank.find("name", bankName) + if(bank.id.get == account.bankID.get) + } yield account + } + + /** + * Add a user generated comment to the transaction. Saves the db model when called. + * + * @param email The email address of the person posting the comment + * @param text The text of the comment + */ + def addComment(userId: String, viewId : Long, text: String, datePosted : Date) : Comment = { + val comment = OBPComment.createRecord.userId(userId). + textField(text). + date(datePosted). + viewID(viewId).save + obp_comments(comment.id.is :: obp_comments.get ).save + comment + } + + def deleteComment(id : String) : Box[Unit]= { + OBPComment.find(id) match { + case Full(comment) => { + if(comment.delete_!){ + obp_comments(obp_comments.get.diff(Seq(new ObjectId(id)))).save + Full() + } + else Failure("Delete not completed") + } + case _ => Failure("Comment "+id+" not found") + } + } + + def addWhereTag(userId: String, viewId : Long, datePosted : Date, longitude : Double, latitude : Double) : Boolean = { + val newTag = OBPGeoTag.createRecord. + userId(userId). + viewID(viewId). + date(datePosted). + geoLongitude(longitude). + geoLatitude(latitude) + + + //before to save the geo tag we need to be sure there is only one per view + //so we look if there is allready a tag with the same view (viewId) + val tags = whereTags.get.find(geoTag => geoTag.viewID equals viewId) match { + case Some(tag) => { + //if true remplace it with the new one + newTag :: whereTags.get.diff(Seq(tag)) + } + case _ => + //else just add this one + newTag :: whereTags.get + } + whereTags(tags).save + true + } + + def deleteWhereTag(viewId : Long):Boolean = { + val where :Option[OBPGeoTag] = whereTags.get.find(loc=>{loc.viewId ==viewId}) + where match { + case Some(w) => { + val newWhereTags = whereTags.get.diff(Seq(w)) + whereTags(newWhereTags).save + true + } + case None => false + } + } + + def addTag(userId: String, viewId : Long, value: String, datePosted : Date) : Tag = { + val tag = OBPTag.createRecord. + userId(userId). + tag(value). + date(datePosted). + viewID(viewId).save + tags(tag.id.is :: tags.get ).save + tag + } + + def deleteTag(id : String) : Box[Unit] = { + OBPTag.find(id) match { + case Full(tag) => { + if(tag.delete_!){ + tags(tags.get.diff(Seq(new ObjectId(id)))).save + Full() + } + else Failure("Delete not completed") + } + case _ => Failure("Tag "+id+" not found") + } + } + + /** + * @return the id of the newly added image + */ + def addImage(userId: String, viewId : Long, description: String, datePosted : Date, imageURL : URL) : TransactionImage = { + val image = OBPTransactionImage.createRecord. + userId(userId).imageComment(description).date(datePosted).viewID(viewId).url(imageURL.toString).save + images(image.id.is :: images.get).save + image + } + + def deleteImage(id : String){ + OBPTransactionImage.find(id) match { + case Full(image) => { + //if(image.postedBy.isDefined && image.postedBy.get.id.get == userId) { + if (image.delete_!) { + logger.info("==> deleted image id : " + id) + images(images.get.diff(Seq(new ObjectId(id)))).save + //TODO: Delete the actual image file? We don't always control the url of the image so we can't always delete it + } + } + case _ => logger.warn("Could not find image with id " + id + " to delete.") + } + } + def orderByDateDescending = (e1: OBPEnvelope, e2: OBPEnvelope) => { val date1 = e1.obp_transaction.get.details.get.completed.get val date2 = e2.obp_transaction.get.details.get.completed.get date1.after(date2) } - - // This creates a json attribute called "obp_transaction" - object obp_transaction extends BsonRecordField(this, OBPTransaction) - - //not named comments as "comments" was used in an older mongo document version - object obp_comments extends ObjectIdRefListField[OBPEnvelope, OBPComment](this, OBPComment) - object narrative extends StringField(this, 255) - + def createAliases : Box[String] = { + val realOtherAccHolder = this.obp_transaction.get.other_account.get.holder.get + + def publicAliasExists(realValue: String): Boolean = { + this.theAccount match { + case Full(a) => { + val otherAccs = a.otherAccounts.objs + val aliasInQuestion = otherAccs.find(o => + o.holder.get.equals(realValue)) + aliasInQuestion.isDefined + } + case _ => false + } + } + + def createPublicAlias(realOtherAccHolder : String) : Box[String] = { + + /** + * Generates a new alias name that is guaranteed not to collide with any existing public alias names + * for the account in question + */ + def newPublicAliasName(account: Account): String = { + val firstAliasAttempt = "ALIAS_" + Random.nextLong().toString.take(6) + + /** + * Returns true if @publicAlias is already the name of a public alias within @account + */ + def isDuplicate(publicAlias: String, account: Account) = { + account.otherAccounts.objs.find(oAcc => { + oAcc.publicAlias.get == publicAlias + }).isDefined + } + + /** + * Appends things to @publicAlias until it a unique public alias name within @account + */ + def appendUntilUnique(publicAlias: String, account: Account): String = { + val newAlias = publicAlias + Random.nextLong().toString.take(1) + if (isDuplicate(newAlias, account)) appendUntilUnique(newAlias, account) + else newAlias + } + + if (isDuplicate(firstAliasAttempt, account)) appendUntilUnique(firstAliasAttempt, account) + else firstAliasAttempt + } + + this.theAccount match { + case Full(a) => { + val randomAliasName = newPublicAliasName(a) + //create a new "otherAccount" + val otherAccount = OtherAccount.createRecord.holder(realOtherAccHolder).publicAlias(randomAliasName).save + a.otherAccounts(otherAccount.id.is :: a.otherAccounts.get).save + Full(randomAliasName) + } + case _ => { + logger.warn("Account not found to create aliases for") + Failure("Account not found to create aliases for") + } + } + } + + if (!publicAliasExists(realOtherAccHolder)) + createPublicAlias(realOtherAccHolder) + else + Full(realOtherAccHolder) + } /** * A JSON representation of the transaction to be returned when successfully added via an API call */ @@ -207,21 +384,23 @@ class OBPEnvelope private() extends MongoRecord[OBPEnvelope] with ObjectIdPk[OBP class OBPComment private() extends MongoRecord[OBPComment] with ObjectIdPk[OBPComment] with Comment { def meta = OBPComment - def postedBy = OBPUser.find(userId) + def postedBy = User.findById(userId.get) def viewId = viewID.get def text = textField.get def datePosted = date.get def id_ = id.is.toString - object userId extends LongField(this) + def replyToID = replyTo.get + object userId extends StringField(this,255) object viewID extends LongField(this) object textField extends StringField(this, 255) object date extends DateField(this) + object replyTo extends StringField(this,255) } object OBPComment extends OBPComment with MongoMetaRecord[OBPComment] object OBPEnvelope extends OBPEnvelope with MongoMetaRecord[OBPEnvelope] with Loggable { - + class OBPQueryParam trait OBPOrder { def orderValue : Int } object OBPOrder { @@ -237,143 +416,28 @@ object OBPEnvelope extends OBPEnvelope with MongoMetaRecord[OBPEnvelope] with Lo case class OBPFromDate(value: Date) extends OBPQueryParam case class OBPToDate(value: Date) extends OBPQueryParam case class OBPOrdering(field: Option[String], order: OBPOrder) extends OBPQueryParam - - override def fromJValue(jval: JValue) = { - def createAliases(env: OBPEnvelope) = { - val realOtherAccHolder = env.obp_transaction.get.other_account.get.holder.get - - def publicAliasExists(realValue: String): Boolean = { - env.theAccount match { - case Full(a) => { - val otherAccs = a.otherAccounts.get - val aliasInQuestion = otherAccs.find(o => - o.holder.get.equals(realValue)) - aliasInQuestion.isDefined - } - case _ => false - } - } - - def privateAliasExists(realValue: String): Boolean = { - env.theAccount match { - case Full(a) => { - val otherAccs = a.otherAccounts.get - val aliasInQuestion = otherAccs.find(o => - o.holder.get.equals(realValue)) - aliasInQuestion.isDefined - } - case _ => false - } - } - - def createPublicAlias() = { - //TODO: Guarantee a unique public alias string - - /** - * Generates a new alias name that is guaranteed not to collide with any existing public alias names - * for the account in question - */ - def newPublicAliasName(account: Account): String = { - val newAlias = "ALIAS_" + Random.nextLong().toString.take(6) - - /** - * Returns true if @publicAlias is already the name of a public alias within @account - */ - def isDuplicate(publicAlias: String, account: Account) = { - account.otherAccounts.get.find(oAcc => { - oAcc.publicAlias.get == newAlias - }).isDefined - } - - /** - * Appends things to @publicAlias until it a unique public alias name within @account - */ - def appendUntilUnique(publicAlias: String, account: Account): String = { - val newAlias = publicAlias + Random.nextLong().toString.take(1) - if (isDuplicate(newAlias, account)) appendUntilUnique(newAlias, account) - else newAlias - } - - if (isDuplicate(newAlias, account)) appendUntilUnique(newAlias, account) - else newAlias - } - - env.theAccount match { - case Full(a) => { - val randomAliasName = newPublicAliasName(a) - val oAccHolderName = env.obp_transaction.get.other_account.get.holder.get - val otherAccount = a.otherAccounts.get.find(acc => acc.holder.equals(oAccHolderName)) - val updatedAccount = otherAccount match { - case Some(o) => { - //update the "otherAccount" - val newOtherAcc = o.publicAlias(randomAliasName) - a.otherAccounts(a.otherAccounts.get -- List(o) ++ List(newOtherAcc)) - } - case _ => { - //create a new "otherAccount" - a.otherAccounts(a.otherAccounts.get ++ List(OtherAccount.createRecord.holder(oAccHolderName).publicAlias(randomAliasName))) - } - } - - updatedAccount.saveTheRecord() - Full(randomAliasName) - } - case _ => { - logger.warn("Account not found to create aliases for") - Empty - } - } - } - - def createPlaceholderPrivateAlias() = { - env.theAccount match { - case Full(a) => { - val oAccHolderName = env.obp_transaction.get.other_account.get.holder.get - val otherAccount = a.otherAccounts.get.find(acc => acc.holder.equals(oAccHolderName)) - val updatedAccount = otherAccount match { - case Some(o) => { - //update the "otherAccount" - val newOtherAcc = o.privateAlias("") - a.otherAccounts(a.otherAccounts.get -- List(o) ++ List(newOtherAcc)) - } - case _ => { - //create a new "otherAccount" - a.otherAccounts(a.otherAccounts.get ++ List(OtherAccount.createRecord.holder(oAccHolderName))) - } - } - updatedAccount.saveTheRecord() - Full("") - } - case _ => Empty - } - } - - - if (!publicAliasExists(realOtherAccHolder)) { - createPublicAlias() - } - if (!privateAliasExists(realOtherAccHolder)) createPlaceholderPrivateAlias() - } - - val created = super.fromJValue(jval) + def envlopesFromJvalue(jval: JValue) : Box[OBPEnvelope] = { + val created = fromJValue(jval) created match { - case Full(c) => createAliases(c) - case _ => //don't create anything + case Full(c) => c.createAliases match { + case Full(alias) => Full(c) + case Failure(msg, _, _ ) => Failure(msg) + case _ => Failure("Alias not created") + } + case _ => Failure("could not create Envelope form JValue") } - created } - } class OBPTransaction private() extends BsonRecord[OBPTransaction]{ def meta = OBPTransaction - + object this_account extends BsonRecordField(this, OBPAccount) object other_account extends BsonRecordField(this, OBPAccount) object details extends BsonRecordField(this, OBPDetails) - + def whenAddedJson(envelopeId : String) : JObject = { JObject(List(JField("obp_transaction_uuid", JString(envelopeId)), JField("this_account", this_account.get.whenAddedJson), @@ -421,7 +485,7 @@ class OBPBank private() extends BsonRecord[OBPBank]{ object national_identifier extends net.liftweb.record.field.StringField(this, 255) object name extends net.liftweb.record.field.StringField(this, 255) - + def whenAddedJson : JObject = { JObject(List( JField("IBAN", JString(IBAN.get)), JField("national_identifier", JString(national_identifier.get)), @@ -443,12 +507,13 @@ class OBPDetails private() extends BsonRecord[OBPDetails]{ object new_balance extends BsonRecordField(this, OBPBalance) object value extends BsonRecordField(this, OBPValue) object completed extends DateField(this) - - + object label extends net.liftweb.record.field.StringField(this, 255) + + def formatDate(date : Date) : String = { OBPDetails.formats.dateFormat.format(date) } - + def whenAddedJson : JObject = { JObject(List( JField("type_en", JString(type_en.get)), JField("type_de", JString(type_de.get)), @@ -466,8 +531,8 @@ object OBPDetails extends OBPDetails with BsonMetaRecord[OBPDetails] class OBPBalance private() extends BsonRecord[OBPBalance]{ def meta = OBPBalance - object currency extends net.liftweb.record.field.StringField(this, 5) - object amount extends net.liftweb.record.field.DecimalField(this, 0) // ok to use decimal? + object currency extends StringField(this, 5) + object amount extends DecimalField(this, 0) // ok to use decimal? def whenAddedJson : JObject = { JObject(List( JField("currency", JString(currency.get)), @@ -482,7 +547,7 @@ class OBPValue private() extends BsonRecord[OBPValue]{ object currency extends net.liftweb.record.field.StringField(this, 5) object amount extends net.liftweb.record.field.DecimalField(this, 0) // ok to use decimal? - + def whenAddedJson : JObject = { JObject(List( JField("currency", JString(currency.get)), JField("amount", JString(amount.get.toString)))) @@ -491,4 +556,62 @@ class OBPValue private() extends BsonRecord[OBPValue]{ object OBPValue extends OBPValue with BsonMetaRecord[OBPValue] +class OBPTag private() extends MongoRecord[OBPTag] with ObjectIdPk[OBPTag] with Tag { + def meta = OBPTag + object userId extends StringField(this,255) + object viewID extends LongField(this) + object tag extends StringField(this, 255) + object date extends DateField(this) + def id_ = id.is.toString + def datePosted = date.get + def postedBy = User.findById(userId.get) + def viewId = viewID.get + def value = tag.get +} + +object OBPTag extends OBPTag with MongoMetaRecord[OBPTag] + +class OBPTransactionImage private() extends MongoRecord[OBPTransactionImage] + with ObjectIdPk[OBPTransactionImage] with TransactionImage { + def meta = OBPTransactionImage + + object userId extends StringField(this,255) + object viewID extends LongField(this) + object imageComment extends StringField(this, 1000) + object date extends DateField(this) + object url extends StringField(this, 500) + + def id_ = id.is.toString + def datePosted = date.get + def postedBy = User.findById(userId.get) + def viewId = viewID.get + def description = imageComment.get + def imageUrl = { + tryo {new URL(url.get)} getOrElse OBPTransactionImage.notFoundUrl + } +} + +object OBPTransactionImage extends OBPTransactionImage with MongoMetaRecord[OBPTransactionImage] { + val notFoundUrl = new URL(S.hostAndPath + "/notfound.png") //TODO: Make this image exist? +} + + +class OBPGeoTag private() extends BsonRecord[OBPGeoTag] with GeoTag { + def meta = OBPGeoTag + + object userId extends StringField(this,255) + object viewID extends LongField(this) + object date extends DateField(this) + + object geoLongitude extends DoubleField(this,0) + object geoLatitude extends DoubleField(this,0) + + def datePosted = date.get + def postedBy = User.findById(userId.get) + def viewId = viewID.get + def longitude = geoLongitude.get + def latitude = geoLatitude.get + +} +object OBPGeoTag extends OBPGeoTag with BsonMetaRecord[OBPGeoTag] \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/dataAccess/OBPUser.scala b/MavLift/src/main/scala/code/model/dataAccess/OBPUser.scala index e277416b3..511f04098 100755 --- a/MavLift/src/main/scala/code/model/dataAccess/OBPUser.scala +++ b/MavLift/src/main/scala/code/model/dataAccess/OBPUser.scala @@ -1,4 +1,4 @@ -/** +/** Open Bank Project - Transparency / Social Finance Web Application Copyright (C) 2011, 2012, TESOBE / Music Pictures Ltd @@ -15,14 +15,14 @@ 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 . -Email: contact@tesobe.com -TESOBE / Music Pictures Ltd +Email: contact@tesobe.com +TESOBE / Music Pictures Ltd Osloerstrasse 16/17 Berlin 13359, Germany This product includes software developed at TESOBE (http://www.tesobe.com/) - by + by Simon Redfern : simon AT tesobe DOT com Stefan Bethge : stefan AT tesobe DOT com Everett Sochowski : everett AT tesobe DOT com @@ -37,11 +37,9 @@ import net.liftweb.common._ import net.liftweb.record.field.StringField import scala.xml.NodeSeq import net.liftweb.sitemap.Loc.LocGroup -import net.liftweb.http.S -import net.liftweb.http.SessionVar +import net.liftweb.http.{S,SessionVar,Templates} import com.mongodb.QueryBuilder -import code.model.traits.{View,BankAccount,User} -import code.model.implementedTraits._ +import code.model.{View,User, BankAccount, OurNetwork, Management, Public, Team, Board, Authorities, Owner} import net.liftweb.json.JsonDSL._ import net.liftweb.http.SHtml import net.liftweb.http.S @@ -56,135 +54,51 @@ import net.liftweb.json.JsonAST.JObject */ class OBPUser extends MegaProtoUser[OBPUser] with User{ def getSingleton = OBPUser // what's the "meta" server - def id_ = id.is.toString + def id_ = emailAddress def emailAddress = email.get - def theFistName : String = firstName.get + def theFirstName : String = firstName.get def theLastName : String = lastName.get + def provider = Props.get("hostname","") + def permittedViews(account: BankAccount): Set[View] = { var views: Set[View] = Set() - if (OBPUser.hasOurNetworkPermission(account)) views = views + OurNetwork - if (OBPUser.hasTeamPermission(account)) views = views + Team - if (OBPUser.hasBoardPermission(account)) views = views + Board - if (OBPUser.hasAuthoritiesPermission(account)) views = views + Authorities - if (OBPUser.hasOwnerPermission(account)) views = views + Owner - if (account.allowAnnoymousAccess) views = views + Anonymous + if (hasOurNetworkPermission(account)) views = views + OurNetwork + if (hasTeamPermission(account)) views = views + Team + if (hasBoardPermission(account)) views = views + Board + if (hasAuthoritiesPermission(account)) views = views + Authorities + if (hasOwnerPermission(account)) views = views + Owner + if (account.allowPublicAccess) views = views + Public views } - + def hasMangementAccess(bankAccount: BankAccount) = { - OBPUser.hasManagementPermission(bankAccount) + hasManagementPermission(bankAccount) } - - def accountsWithMoreThanAnonAccess : Set[BankAccount] = { - - val hostedAccountTable = HostedAccount._dbTableNameLC - val privilegeTable = Privilege._dbTableNameLC - val userTable = OBPUser._dbTableNameLC - - val hostedId = hostedAccountTable + "." + HostedAccount.id.dbColumnName - val hostedAccId = hostedAccountTable + "." + HostedAccount.accountID.dbColumnName - val privilegeAccId = privilegeTable + "." + Privilege.account.dbColumnName - val privilegeUserId = privilegeTable + "." + Privilege.user.dbColumnName - val userId = this.id.get - - val ourNetworkPrivilege = privilegeTable + "." + Privilege.ourNetworkPermission.dbColumnName - val teamPrivilege = privilegeTable + "." + Privilege.teamPermission.dbColumnName - val boardPrivilege = privilegeTable + "." + Privilege.boardPermission.dbColumnName - val authoritiesPrivilege = privilegeTable + "." + Privilege.authoritiesPermission.dbColumnName - val ownerPrivilege = privilegeTable + "." + Privilege.ownerPermission.dbColumnName - - val query = "SELECT " + hostedId + ", " + hostedAccId + - " FROM " + hostedAccountTable + ", " + privilegeTable + ", " + userTable + - " WHERE " + "( " + hostedId + " = " + privilegeAccId + ")" + - " AND " + "( " + privilegeUserId + " = " + userId + ")" + - " AND " + "( " + ourNetworkPrivilege + " = true" + - " OR " + teamPrivilege + " = true" + - " OR " + boardPrivilege + " = true" + - " OR " + authoritiesPrivilege + " = true" + - " OR " + ownerPrivilege + " = true)" - - val moreThanAnon = HostedAccount.findAllByInsecureSql(query, IHaveValidatedThisSQL("everett", "nov. 15 2012")) - val mongoIds = moreThanAnon.map(hAcc => new ObjectId(hAcc.accountID.get)) - - Account.findAll(mongoIds).map(Account.toBankAccount).toSet - } -} -/** - * The singleton that has methods for accessing the database - */ -object OBPUser extends OBPUser with MetaMegaProtoUser[OBPUser]{ - - override def dbTableName = "users" // define the DB table name - - override def screenWrap = Full( - ) - // define the order fields will appear in forms and output - override def fieldOrder = List(id, firstName, lastName, email, - locale, timezone, password) - - // comment this line out to require email validations - override def skipEmailValidation = true - - //Keep track of the referer on login - object loginReferer extends SessionVar("/") - //This is where the user gets redirected to after login - override def homePage = { - var ret = loginReferer.is - loginReferer.remove() - ret - } - - override def loginXhtml = { - import net.liftweb.http.TemplateFinder - import net.liftweb.http.js.JsCmds.Noop - val loginXml = TemplateFinder.findAnyTemplate(List("templates-hidden","_login")).map({ - "form [action]" #> {S.uri} & - "#loginText * " #> {S.??("log.in")} & - "#emailAddressText * " #> {S.??("email.address")} & - "#passwordText * " #> {S.??("password")} & - "#recoverPasswordLink * " #> { - "a [href]" #> {lostPasswordPath.mkString("/", "/", "")} & - "a *" #> {S.??("recover.password")} - } & - "#SignUpLink * " #> { - "a [href]" #> {OBPUser.signUpPath.foldLeft("")(_ + "/" + _)} & - "a *" #> {S.??("sign.up")} - } - }) - SHtml.span(loginXml getOrElse NodeSeq.Empty,Noop) - } - - //Set the login referer - override def login = { - for(r <- S.referer if loginReferer.is.equals("/")) loginReferer.set(r) - super.login - } - def hasOurNetworkPermission(account: BankAccount) : Boolean = { hasPermission(account, (p: Privilege) => p.ourNetworkPermission.is) } - + def hasTeamPermission(account: BankAccount) : Boolean = { hasPermission(account, (p: Privilege) => p.teamPermission.is) } - + def hasBoardPermission(account: BankAccount) : Boolean = { hasPermission(account, (p: Privilege) => p.boardPermission.is) } - + def hasAuthoritiesPermission(account: BankAccount) : Boolean = { hasPermission(account, (p: Privilege) => p.authoritiesPermission.is) } - + def hasOwnerPermission(account: BankAccount) : Boolean = { hasPermission(account, (p: Privilege) => p.ownerPermission.is) } def hasManagementPermission(account: BankAccount) : Boolean = { hasPermission(account, (p: Privilege) => p.mangementPermission.is) } - + def hasMoreThanAnonAccess(account: BankAccount) : Boolean = { OBPUser.hasAuthoritiesPermission(account) || OBPUser.hasBoardPermission(account) || @@ -193,23 +107,72 @@ object OBPUser extends OBPUser with MetaMegaProtoUser[OBPUser]{ OBPUser.hasTeamPermission(account) || OBPUser.hasManagementPermission(account) } - - def hasPermission(bankAccount: BankAccount, permissionCheck: (Privilege) => Boolean) : Boolean = { - currentUser match{ - case Full(u) => - HostedAccount.find(By(HostedAccount.accountID, bankAccount.id)) match { - case Full(hostedAccount) => - Privilege.find(By(Privilege.account, hostedAccount), By(Privilege.user, u)) match{ - case Full(p) => permissionCheck(p) - case _ => false - } - case _ => false + + def hasPermission(bankAccount: BankAccount, permissionCheck: (Privilege) => Boolean): Boolean = { + HostedAccount.find(By(HostedAccount.accountID, bankAccount.id)) match { + case Full(hostedAccount) => + Privilege.find(By(Privilege.account, hostedAccount), By(Privilege.user, this)) match { + case Full(p) => permissionCheck(p) + case _ => false } case _ => false } } } +/** + * The singleton that has methods for accessing the database + */ +object OBPUser extends OBPUser with MetaMegaProtoUser[OBPUser]{ + + override def dbTableName = "users" // define the DB table name + + override def screenWrap = Full( + ) + // define the order fields will appear in forms and output + override def fieldOrder = List(id, firstName, lastName, email, + locale, timezone, password) + + // comment this line out to require email validations + override def skipEmailValidation = true + + //Keep track of the referer on login + object loginReferer extends SessionVar("/") + //This is where the user gets redirected to after login + override def homePage = { + var ret = loginReferer.is + loginReferer.remove() + ret + } + + override def loginXhtml = { + import net.liftweb.http.TemplateFinder + import net.liftweb.http.js.JsCmds.Noop + val loginXml = Templates(List("templates-hidden","_UserLogin")).map({ + "form [action]" #> {S.uri} & + "#loginText * " #> {S.??("log.in")} & + "#emailAddressText * " #> {S.??("email.address")} & + "#passwordText * " #> {S.??("password")} & + "#recoverPasswordLink * " #> { + "a [href]" #> {lostPasswordPath.mkString("/", "/", "")} & + "a *" #> {S.??("recover.password")} + } & + "#SignUpLink * " #> { + "a [href]" #> {OBPUser.signUpPath.foldLeft("")(_ + "/" + _)} & + "a *" #> {S.??("sign.up")} + } + }) + SHtml.span(loginXml getOrElse NodeSeq.Empty,Noop) + } + + //Set the login referer + override def login = { + for(r <- S.referer if loginReferer.is.equals("/")) loginReferer.set(r) + super.login + } + +} + /** * Yes, MappedBoolean has a default value of false, but in the very small chance * that this changes, we won't break any authentication. @@ -221,14 +184,14 @@ class ourMappedBoolean[T<:Mapper[T]](fieldOwner: T) extends MappedBoolean[T](fie class Privilege extends LongKeyedMapper[Privilege] with CreatedUpdated{ def getSingleton = Privilege def primaryKeyField = id - object id extends MappedLongIndex(this) + object id extends MappedLongIndex(this) object user extends MappedLongForeignKey(this, OBPUser){ var userError = false override def validSelectValues = Full(OBPUser.findMap(OrderBy(OBPUser.email, Ascending)){ case u: User => Full(u.id.is -> u.email.is) }) - override def displayHtml = User email + override def displayHtml = User email override def asHtml = { val email = (for { u <- OBPUser.find(user.get) @@ -236,35 +199,35 @@ class Privilege extends LongKeyedMapper[Privilege] with CreatedUpdated{ {email} } - def userEmailCheck(user : Long) : List[FieldError]= - if(userError) List(FieldError(this, "No user with this email")) + def userEmailCheck(user : Long) : List[FieldError]= + if(userError) List(FieldError(this, "No user with this email")) else Nil override def validations = userEmailCheck _ :: super.validations override def _toForm = - { + { val initialValue = user.obj match { case Full(theUser) => theUser.email.is - case _ => "" + case _ => "" } - def saveTheUser(email : String) = + def saveTheUser(email : String) = OBPUser.find(By(OBPUser.email, email)) match { case Full(theUser) => user(theUser) case _ => userError=true - } + } Full(SHtml.text(initialValue, saveTheUser(_))) } } - + object account extends MappedLongForeignKey(this, HostedAccount){ - - override def displayHtml = Account + + override def displayHtml = Account override def asHtml = { { HostedAccount.find(account.get) match { case Full(account) => account.bank + " - " + account.name case _ => "account not found" } - } + } } override def validSelectValues = Full( @@ -275,7 +238,7 @@ class Privilege extends LongKeyedMapper[Privilege] with CreatedUpdated{ case privilege: Privilege => HostedAccount.find(privilege.account.is) match { case Full(hosted) => Full(hosted.id.is -> (hosted.bank + " - "+ hosted.name + " - " + hosted.number) ) case _ => Empty - } + } } case _ => List() } @@ -305,22 +268,22 @@ class Privilege extends LongKeyedMapper[Privilege] with CreatedUpdated{ object Privilege extends Privilege with LongKeyedMetaMapper[Privilege] with CRUDify[Long, Privilege]{ override def calcPrefix = List("admin",_dbTableNameLC) override def fieldOrder = List(account, user,updatedAt, ownerPermission, mangementPermission, - ourNetworkPermission, teamPermission, boardPermission) + ourNetworkPermission, teamPermission, boardPermission) override def displayName = "Privilege" override def showAllMenuLocParams = LocGroup("admin") :: Nil override def createMenuLocParams = LocGroup("admin") :: Nil - override def fieldsForDisplay = super.fieldsForDisplay -- List(createdAt) - override def fieldsForEditing = super.fieldsForEditing -- List(createdAt, updatedAt) + override def fieldsForDisplay = super.fieldsForDisplay filterNot (List(createdAt) contains) + override def fieldsForEditing = super.fieldsForEditing filterNot (List(createdAt, updatedAt) contains) def showAll = doCrudAll(_) override def findForList(start : Long, count : Int)= { OBPUser.currentUser match { case Full(user) => { - def ownerPermissionTest(privilege : Privilege) : Boolean = + def ownerPermissionTest(privilege : Privilege) : Boolean = Privilege.find(By(Privilege.user, user), By(Privilege.account, privilege.account)) match { case Full(currentUserPrivilege) => currentUserPrivilege.ownerPermission - case _ => false + case _ => false } - //we show only the privileges that concernes accounts were the current user + //we show only the privileges that concernes accounts were the current user //has owner permissions on //TODO: This is inefficient (it loads all privileges) Privilege.findAll(OrderBy(Privilege.account, Ascending)).filter(ownerPermissionTest _) @@ -332,15 +295,15 @@ object Privilege extends Privilege with LongKeyedMetaMapper[Privilege] with CRUD class HostedAccount extends LongKeyedMapper[HostedAccount] { def getSingleton = HostedAccount def primaryKeyField = id - - object id extends MappedLongIndex(this) + + object id extends MappedLongIndex(this) object accountID extends MappedString(this, 255) def theAccount = Account.find(("_id", accountID.toString)) def name : String= theAccount match { case Full(account) => account.name.get.toString() - case _ => "" + case _ => "" } def bank : String = theAccount match { case Full(account) => account.bankName @@ -349,7 +312,7 @@ class HostedAccount extends LongKeyedMapper[HostedAccount] { def number : String = theAccount match { case Full(account) => account.number.get case _ => "" - } + } } object HostedAccount extends HostedAccount with LongKeyedMetaMapper[HostedAccount]{} diff --git a/MavLift/src/main/scala/code/model/traits/OtherBankAccount.scala b/MavLift/src/main/scala/code/model/dataAccess/metrics.scala similarity index 71% rename from MavLift/src/main/scala/code/model/traits/OtherBankAccount.scala rename to MavLift/src/main/scala/code/model/dataAccess/metrics.scala index fd238b718..257eb6479 100644 --- a/MavLift/src/main/scala/code/model/traits/OtherBankAccount.scala +++ b/MavLift/src/main/scala/code/model/dataAccess/metrics.scala @@ -29,18 +29,17 @@ Berlin 13359, Germany Ayoub Benali: ayoub AT tesobe DOT com */ + + package code.model.dataAccess -package code.model.traits + import net.liftweb.mongodb.record.field.{ObjectIdPk,DateField} + import net.liftweb.record.field.{StringField} + import net.liftweb.mongodb.record.{MongoRecord,MongoMetaRecord} -trait OtherBankAccount { - - def id : String - //account holder hame - def label : String - def nationalIdentifier : String - def bankName : String - def number : String - def swift_bic : Option[String] - def iban : Option[String] - def metadata : OtherBankAccountMetadata +class APIMetric extends MongoRecord[APIMetric] with ObjectIdPk[APIMetric] { + def meta = APIMetric + object url extends StringField(this,255) + object date extends DateField(this) } + +object APIMetric extends APIMetric with MongoMetaRecord[APIMetric] diff --git a/MavLift/src/main/scala/code/model/implementedTraits/BankAccountImpl.scala b/MavLift/src/main/scala/code/model/implementedTraits/BankAccountImpl.scala deleted file mode 100644 index e1fd8dda3..000000000 --- a/MavLift/src/main/scala/code/model/implementedTraits/BankAccountImpl.scala +++ /dev/null @@ -1,124 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.implementedTraits - -import scala.math.BigDecimal -import java.util.Date -import scala.collection.immutable.Set -import net.liftweb.json.JsonDSL._ -import net.liftweb.common.Full -import sun.reflect.generics.reflectiveObjects.NotImplementedException -import code.model.traits.{BankAccount,AccountOwner, Transaction} -import code.model.dataAccess.{Account,OBPEnvelope} -import code.model.traits.Transaction -import code.model.traits.ModeratedTransaction -import code.model.dataAccess.OBPEnvelope.OBPQueryParam -import net.liftweb.common.Box -import code.model.dataAccess.LocalStorage -import code.model.dataAccess.OBPEnvelope._ -import code.model.dataAccess.OBPUser -import code.model.traits.User - -class BankAccountImpl(id_ : String, var _owners : Set[AccountOwner], accountType_ : String, - currency_ : String, label_ : String, nationalIdentifier_ : String, swift_bic_ : Option[String], - iban_ : Option[String], allowAnnoymousAccess_ : Boolean, - number_ : String, bankName_ : String, bankPermalink_ : String, permalink_ : String) extends BankAccount { - - def id = id_ - def owners = _owners - def owners_=(owners_ : Set[AccountOwner]) = _owners = owners_ - def accountType = accountType_ - def balance = { - val newest = getTransactions(OBPLimit(1), OBPOrdering(None, OBPDescending)) - newest match { - case Full(n) => { - n match { - case Nil => None - case x :: xs => Some(x.balance) - } - } - case _ => None - } - } - def bankPermalink = bankPermalink_ - def permalink = permalink_ - def currency = currency_ - def label = label_ - def nationalIdentifier = nationalIdentifier_ - def swift_bic = swift_bic_ - def iban = iban_ - def number = number_ - def bankName = bankName_ - - def permittedViews(user: Box[User]) : Set[code.model.traits.View] = { - user match { - case Full(u) => u.permittedViews(this) - case _ => if(this.allowAnnoymousAccess) Set(Anonymous) else Set() - } - } - - def transactions(from: Date, to: Date): Set[Transaction] = { - throw new NotImplementedException - } - def transaction(id: String): Box[Transaction] = { - LocalStorage.getTransaction(id, bankPermalink, permalink) - } - def allowAnnoymousAccess = allowAnnoymousAccess_ - - def getModeratedTransactions(moderate: Transaction => ModeratedTransaction): List[ModeratedTransaction] = { - LocalStorage.getModeratedTransactions(permalink, bankPermalink)(moderate) - } - - def getModeratedTransactions(queryParams: OBPQueryParam*)(moderate: Transaction => ModeratedTransaction): List[ModeratedTransaction] = { - LocalStorage.getModeratedTransactions(permalink, bankPermalink, queryParams: _*)(moderate) - } - - def getTransactions(queryParams: OBPQueryParam*) : Box[List[Transaction]] = { - LocalStorage.getTransactions(permalink, bankPermalink, queryParams: _*) - } - - def getTransactions(bank: String, account: String): Box[List[Transaction]] = { - LocalStorage.getTransactions(permalink, bankPermalink) - } - - def authorisedAccess(view: code.model.traits.View, user: Option[OBPUser]) = { - view match { - case Anonymous => allowAnnoymousAccess - case _ => user match { - case Some(u) => u.permittedViews(this).contains(view) - case _ => false - } - } - } - -} - diff --git a/MavLift/src/main/scala/code/model/implementedTraits/BankImpl.scala b/MavLift/src/main/scala/code/model/implementedTraits/BankImpl.scala deleted file mode 100644 index d6621f52e..000000000 --- a/MavLift/src/main/scala/code/model/implementedTraits/BankImpl.scala +++ /dev/null @@ -1,43 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.implementedTraits - -import code.model.traits.{Bank, BankAccount} -import code.model.dataAccess.LocalStorage - -class BankImpl(_id: String, _name : String, _permalink : String) extends Bank -{ - def id = _id - def name = _name - def permalink = _permalink - def accounts = LocalStorage.getBankAccounts(this) -} diff --git a/MavLift/src/main/scala/code/model/implementedTraits/MetadataImpl.scala b/MavLift/src/main/scala/code/model/implementedTraits/MetadataImpl.scala deleted file mode 100644 index 46b1fc21b..000000000 --- a/MavLift/src/main/scala/code/model/implementedTraits/MetadataImpl.scala +++ /dev/null @@ -1,59 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.implementedTraits - -import code.model.traits.{Comment, OtherBankAccountMetadata, TransactionMetadata} -import code.model.dataAccess.{OtherAccount, OBPComment} -import net.liftweb.common.Loggable -import java.util.Date - -class OtherBankAccountMetadataImpl(_publicAlias : String, _privateAlias : String,_moreInfo : String, -_url : String, _imageUrl : String, _openCorporatesUrl : String) extends OtherBankAccountMetadata { - - def publicAlias : String = _publicAlias - def privateAlias : String = _privateAlias - def moreInfo : String = _moreInfo - def url : String = _url - def imageUrl : String = _imageUrl - def openCorporatesUrl : String = _openCorporatesUrl -} -class TransactionMetadataImpl(narative : String, comments_ : List[Comment], - saveOwnerComment : String => Unit, addCommentFunc : (Long,Long, String, Date) => Unit ) - extends TransactionMetadata with Loggable -{ - def ownerComment = if(! narative.isEmpty) Some(narative) else None - def ownerComment(comment : String) = saveOwnerComment(comment) - def comments : List[Comment] = comments_ - def addComment(userId: Long, viewId : Long, text: String, datePosted : Date) : Unit = - addCommentFunc(userId, viewId, text, datePosted) -} - diff --git a/MavLift/src/main/scala/code/model/implementedTraits/OtherBankAccountImpl.scala b/MavLift/src/main/scala/code/model/implementedTraits/OtherBankAccountImpl.scala deleted file mode 100644 index 11fa9d539..000000000 --- a/MavLift/src/main/scala/code/model/implementedTraits/OtherBankAccountImpl.scala +++ /dev/null @@ -1,49 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.implementedTraits - -import code.model.traits.{OtherBankAccountMetadata,OtherBankAccount} - -class OtherBankAccountImpl(id_ : String, label_ : String, nationalIdentifier_ : String, - swift_bic_ : Option[String], iban_ : Option[String], number_ : String, - bankName_ : String, metadata_ : OtherBankAccountMetadata) extends OtherBankAccount -{ - - def id = id_ - def label = label_ - def nationalIdentifier = nationalIdentifier_ - def swift_bic = swift_bic_ - def iban = iban_ - def number = number_ - def bankName = bankName_ - def metadata = metadata_ -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/implementedTraits/TransactionImpl.scala b/MavLift/src/main/scala/code/model/implementedTraits/TransactionImpl.scala deleted file mode 100644 index e1a08f3f0..000000000 --- a/MavLift/src/main/scala/code/model/implementedTraits/TransactionImpl.scala +++ /dev/null @@ -1,59 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.implementedTraits - -import code.model.dataAccess.{OBPEnvelope,OBPTransaction,OtherAccount} -import code.model.traits.{Transaction,BankAccount,OtherBankAccount, TransactionMetadata} -import scala.math.BigDecimal -import java.util.Date -import scala.collection.immutable.List -import net.liftweb.common.Loggable -import net.liftweb.common.Box -import code.model.traits.Comment - -class TransactionImpl(id_ : String, var _thisAccount : BankAccount = null, otherAccount_ : OtherBankAccount, - metadata_ : TransactionMetadata, transactionType_ : String, amount_ : BigDecimal, currency_ : String, - label_ : Option[String], startDate_ : Date, finishDate_ : Date, balance_ : BigDecimal) extends Transaction with Loggable { - - def id = id_ - def thisAccount = _thisAccount - def thisAccount_= (newThisAccount : BankAccount) = _thisAccount = newThisAccount - def otherAccount = otherAccount_ - def metadata = metadata_ - def transactionType = transactionType_ - def amount = amount_ - def currency = currency_ - def label = label_ - def startDate = startDate_ - def finishDate = finishDate_ - def balance = balance_ -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/implementedTraits/ViewsImpl.scala b/MavLift/src/main/scala/code/model/implementedTraits/ViewsImpl.scala deleted file mode 100644 index 5710f9c34..000000000 --- a/MavLift/src/main/scala/code/model/implementedTraits/ViewsImpl.scala +++ /dev/null @@ -1,223 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.implementedTraits - -import code.model.traits._ -import net.liftweb.common.{Box,Empty, Full} -import net.liftweb.json.JsonDSL._ -import net.liftweb.json.JsonAST.JObject - -object View { - //transforme the url into a view - //TODO : load the view from the Data base - def fromUrl(viewNameURL: String): Box[View] = - viewNameURL match { - case "authorities" => Full(Authorities) - case "board" => Full(Board) - case "our-network" => Full(OurNetwork) - case "team" => Full(Team) - case "owner" => Full(Owner) - case "anonymous" => Full(Anonymous) - case _ => Empty - } - - def linksJson(views: Set[View], accountPermalink: String, bankPermalink: String): JObject = { - val viewsJson = views.map(view => { - ("rel" -> "account") ~ - ("href" -> { "/" + bankPermalink + "/account/" + accountPermalink + "/" + view.permalink }) ~ - ("method" -> "GET") ~ - ("title" -> "Get information about one account") - }) - - ("links" -> viewsJson) - } -} -object Team extends FullView { - override def id = 3 - override def name = "Team" - override def permalink = "team" - override def description = "A view for team members related to the account. E.g. for a company bank account -> employees/contractors" - override def canEditOwnerComment= false -} -object Board extends FullView { - override def id = 4 - override def name = "Board" - override def permalink = "board" - override def description = "A view for board members of a company to view that company's account data." - override def canEditOwnerComment= false -} -object Authorities extends FullView { - override def id = 5 - override def name = "Authorities" - override def permalink = "authorities" - override def description = "A view for authorities such as tax officials to view an account's data" - override def canEditOwnerComment= false -} - -object Anonymous extends BaseView { - //the actual class extends the BaseView but in fact it does not matters be cause we don't care about the values - //of the canSeeMoreInfo, canSeeUrl,etc attributes and we implement a specific moderate method - - /** - * Current rules: - * - * If anonymous, and a public alias exists : Show the public alias - * If anonymous, and no public alias exists : Show the real account holder - * If our network, and a private alias exists : Show the private alias - * If our network, and no private alias exists : Show the real account holder - */ - override def id = 6 - override def name = "Anonymous" - override def permalink = "anonymous" - override def description = "A view of the account accessible by anyone." - - //Bank account fields - override def canSeeBankAccountOwners = true - override def canSeeBankAccountType = true - override def canSeeBankAccountBalancePositiveOrNegative = true - override def canSeeBankAccountCurrency = true - override def canSeeBankAccountLabel = true - override def canSeeBankAccountNationalIdentifier = true - override def canSeeBankAccountSwift_bic = true - override def canSeeBankAccountIban = true - override def canSeeBankAccountNumber = true - override def canSeeBankAccountName = true - - override def moderate(transaction: Transaction): ModeratedTransaction = { - - val transactionId = transaction.id //toString().startsWith("-")) "-" else "+" - val accountBalance = "" //not used when displaying transactions, but we might eventually need it. if so, we need a ref to - //the bank account so we could do something like if(canSeeBankAccountBalance) bankAccount.balance else if - // canSeeBankAccountBalancePositiveOrNegative {show + or -} else "" - val thisBankAccount = Some(new ModeratedBankAccount(transaction.thisAccount.id, - Some(transaction.thisAccount.owners), Some(transaction.thisAccount.accountType), - accountBalance, Some(transaction.thisAccount.currency), - Some(transaction.thisAccount.label),None, None, None, Some(transaction.thisAccount.number), - Some(transaction.thisAccount.bankName))) - val otherBankAccount = { - val otherAccountLabel = { - val publicAlias = transaction.otherAccount.metadata.publicAlias - if(publicAlias.isEmpty) - AccountName(transaction.otherAccount.label, NoAlias) - else - AccountName(publicAlias, Public) - } - val otherAccountMetadata = { - def isPublicAlias = otherAccountLabel.aliasType match { - case Public => true - case _ => false - } - val moreInfo = if (isPublicAlias) None else Some(transaction.otherAccount.metadata.moreInfo) - val url = if (isPublicAlias) None else Some(transaction.otherAccount.metadata.url) - val imageUrl = if (isPublicAlias) None else Some(transaction.otherAccount.metadata.imageUrl) - val openCorporatesUrl = if (isPublicAlias) None else Some(transaction.otherAccount.metadata.openCorporatesUrl) - - Some(new ModeratedOtherBankAccountMetadata(moreInfo, url, imageUrl, openCorporatesUrl)) - } - - Some(new ModeratedOtherBankAccount(transaction.otherAccount.id,otherAccountLabel,None,None, - None, None, None, otherAccountMetadata)) - } - val transactionMetadata = Some(new ModeratedTransactionMetadata( - transaction.metadata.ownerComment,Some(transaction.metadata.comments.filter(comment => comment.viewId==id)), - None,Some(transaction.metadata.addComment _))) - val transactionType = Some(transaction.transactionType) - val transactionAmount = Some(transaction.amount) - val transactionCurrency = Some(transaction.currency) - val transactionLabel = Some(transaction.label) - val transactionStartDate = Some(transaction.startDate) - val transactionFinishDate = Some(transaction.finishDate) - val transactionBalance = if (transaction.balance.toString().startsWith("-")) "-" else "+" - - new ModeratedTransaction(transactionId, thisBankAccount, otherBankAccount, transactionMetadata, - transactionType, transactionAmount, transactionCurrency, transactionLabel, transactionStartDate, - transactionFinishDate, transactionBalance) - - } - -} - - object OurNetwork extends BaseView - { - override def id = 7 - override def name = "Our Network" - override def permalink ="our-network" - override def description = "A view for people related to the account in some way. E.g. for a company account this could include investors" + - " or current/potential clients" - override def moderate(transaction: Transaction): ModeratedTransaction = { - val transactionId = transaction.id - val accountBalance = "" //not used when displaying transactions, but we might eventually need it. if so, we need a ref to - //the bank account so we could do something like if(canSeeBankAccountBalance) bankAccount.balance else if - // canSeeBankAccountBalancePositiveOrNegative {show + or -} else "" - val thisBankAccount = Some(new ModeratedBankAccount(transaction.thisAccount.id, None, None, - accountBalance, Some(transaction.thisAccount.currency), - Some(transaction.thisAccount.label),None, None, None, Some(transaction.thisAccount.number), - Some(transaction.thisAccount.bankName))) - val otherBankAccount = { - val otherAccountLabel = { - val privateAlias = transaction.otherAccount.metadata.privateAlias - if(privateAlias.isEmpty) - AccountName(transaction.otherAccount.label, NoAlias) - else - AccountName(privateAlias, Private) - } - val otherAccountMetadata = - Some(new ModeratedOtherBankAccountMetadata(Some(transaction.otherAccount.metadata.moreInfo), - Some(transaction.otherAccount.metadata.url), Some(transaction.otherAccount.metadata.imageUrl), - Some(transaction.otherAccount.metadata.openCorporatesUrl))) - - Some(new ModeratedOtherBankAccount(transaction.otherAccount.id,otherAccountLabel,None,None,None, - None, None, otherAccountMetadata)) - } - val transactionMetadata = Some(new ModeratedTransactionMetadata(transaction.metadata.ownerComment, - Some(transaction.metadata.comments.filter(comment => comment.viewId==id)),None,Some(transaction.metadata.addComment _))) - - val transactionType = Some(transaction.transactionType) - val transactionAmount = Some(transaction.amount) - val transactionCurrency = Some(transaction.currency) - val transactionLabel = Some(transaction.label) - val transactionStartDate = Some(transaction.startDate) - val transactionFinishDate = Some(transaction.finishDate) - val transactionBalance = transaction.balance.toString() - - new ModeratedTransaction(transactionId, thisBankAccount, otherBankAccount, transactionMetadata, - transactionType, transactionAmount, transactionCurrency, transactionLabel, transactionStartDate, - transactionFinishDate, transactionBalance) - } - } - -object Owner extends FullView { - override def id = 8 - override def name="Owner" - override def permalink = "owner" -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/traits/AccountOwner.scala b/MavLift/src/main/scala/code/model/traits/AccountOwner.scala deleted file mode 100644 index f89c3cc8d..000000000 --- a/MavLift/src/main/scala/code/model/traits/AccountOwner.scala +++ /dev/null @@ -1,43 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.traits - -trait AccountOwner { - - def id : String - - def name : String - - def bankAccounts : Set[BankAccount] - -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/traits/Bank.scala b/MavLift/src/main/scala/code/model/traits/Bank.scala deleted file mode 100644 index 7cb3c526e..000000000 --- a/MavLift/src/main/scala/code/model/traits/Bank.scala +++ /dev/null @@ -1,76 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.traits -import net.liftweb.common.Box -import code.model.dataAccess.LocalStorage -import net.liftweb.json.JsonAST.JObject -import net.liftweb.json.JsonDSL._ -import net.liftweb.json.JsonAST.JArray - -trait Bank -{ - def id : String - def name : String - def permalink : String - def accounts : Set[BankAccount] - - def detailedJson : JObject = { - ("name" -> name) ~ - ("website" -> "") ~ - ("email" -> "") - } - - def toJson : JObject = { - ("alias" -> permalink) ~ - ("name" -> name) ~ - ("logo" -> "") ~ - ("links" -> linkJson) - } - - def linkJson : JObject = { - ("rel" -> "bank") ~ - ("href" -> {"/" + permalink + "/bank"}) ~ - ("method" -> "GET") ~ - ("title" -> {"Get information about the bank identified by " + permalink}) - } -} - -object Bank { - def apply(bankPermalink: String) : Box[Bank] = LocalStorage.getBank(bankPermalink) - - def all : List[Bank] = LocalStorage.allBanks - - def toJson(banks: Seq[Bank]) : JArray = - banks.map(bank => bank.toJson) - -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/traits/BankAccount.scala b/MavLift/src/main/scala/code/model/traits/BankAccount.scala deleted file mode 100644 index 3b8d468ce..000000000 --- a/MavLift/src/main/scala/code/model/traits/BankAccount.scala +++ /dev/null @@ -1,131 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.traits -import scala.math.BigDecimal -import java.util.Date -import net.liftweb.common.Box -import code.model.dataAccess.LocalStorage -import net.liftweb.common.{Full, Empty} -import code.model.dataAccess.Account -import code.model.dataAccess.OBPEnvelope.OBPQueryParam -import code.model.dataAccess.OBPUser -import net.liftweb.json.JObject -import net.liftweb.json.JsonDSL._ -import net.liftweb.http.LiftResponse -import net.liftweb.http.JsonResponse -import code.model.implementedTraits.View - -trait BankAccount { - - def id : String - - var owners : Set[AccountOwner] - - //e.g. chequing, savings - def accountType : String - - //TODO: Check if BigDecimal is an appropriate data type - def balance : Option[BigDecimal] - - //ISO 4217, e.g. EUR, GBP, USD, etc. - def currency: String - - //Name to display, e.g. TESOBE Postbank Account - def label : String - - def bankName : String - - def bankPermalink : String - - def permalink : String - - def number: String - - def nationalIdentifier : String - - def swift_bic : Option[String] - - def iban : Option[String] - - def transaction(id: String) : Box[Transaction] - - def moderatedTransaction(id: String, view: View, user: Box[User]) : Box[ModeratedTransaction] = { - if(permittedViews(user).contains(view)) { - transaction(id).map(view.moderate) - } else Empty - } - - def moderatedBankAccount(view: View, user: Box[User]) : Box[ModeratedBankAccount] = { - if(permittedViews(user).contains(view)){ - view.moderate(this) - } else Empty - } - - //Is an anonymous view available for this bank account - def allowAnnoymousAccess : Boolean - - def permittedViews(user: Box[User]) : Set[View] - - def getModeratedTransactions(moderate: Transaction => ModeratedTransaction): List[ModeratedTransaction] - - def getModeratedTransactions(queryParams: OBPQueryParam*)(moderate: Transaction => ModeratedTransaction): List[ModeratedTransaction] - - def authorisedAccess(view: View, user: Option[OBPUser]) : Boolean - - def overviewJson(user: Box[User]): JObject = { - val views = permittedViews(user) - ("number" -> number) ~ - ("account_alias" -> label) ~ - ("owner_description" -> "") ~ - ("views_available" -> views.map(view => view.toJson)) ~ - View.linksJson(views, permalink, bankPermalink) - } -} - -object BankAccount { - def apply(bankpermalink: String, bankAccountPermalink: String) : Box[BankAccount] = { - LocalStorage.getAccount(bankpermalink, bankAccountPermalink) match { - case Full(account) => Full(Account.toBankAccount(account)) - case _ => Empty - } - } - - def all : List[BankAccount] = { - LocalStorage.getAllAccounts() map Account.toBankAccount - } - - def publicAccounts : List[BankAccount] = { - LocalStorage.getAllPublicAccounts() map Account.toBankAccount - } - -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/traits/Comment.scala b/MavLift/src/main/scala/code/model/traits/Comment.scala deleted file mode 100644 index 8a03a650a..000000000 --- a/MavLift/src/main/scala/code/model/traits/Comment.scala +++ /dev/null @@ -1,67 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.traits -import java.util.Date -import net.liftweb.common.{Box,Full} -import net.liftweb.json.JsonAST.JObject -import net.liftweb.json.JsonDSL._ - -trait Comment { - def id_ : String - // The person that posted the comment - def postedBy : Box[User] - - //the id of the view related to the comment - def viewId : Long - - // The actual text of the comment - def text : String - - def datePosted : Date - - def toJson : JObject = { - val userInJson = postedBy match { - case Full(user) => user.toJson - case _ => ("id" -> "") ~ - ("provider" -> "") ~ - ("display_name" -> "") - } - - ("id" -> id_) ~ - ("date" -> datePosted.toString) ~ - ("comment" -> text) ~ - ("view" -> viewId) ~ - ("user" -> userInJson) ~ - ("reply_to" -> "") - } -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/traits/Metadata.scala b/MavLift/src/main/scala/code/model/traits/Metadata.scala deleted file mode 100644 index 453e4ce00..000000000 --- a/MavLift/src/main/scala/code/model/traits/Metadata.scala +++ /dev/null @@ -1,52 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.traits -import java.util.Date - -trait TransactionMetadata { - - // Owner provided comment, done in OBP - def ownerComment : Option[String] - def ownerComment(comment : String) : Unit - def comments : List[Comment] - def addComment(userId : Long, viewId : Long, text : String, postedDate : Date) : Unit -} -trait OtherBankAccountMetadata -{ - def publicAlias : String - def privateAlias : String - def moreInfo : String - def url : String - def imageUrl : String - def openCorporatesUrl : String -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/traits/Moderated.scala b/MavLift/src/main/scala/code/model/traits/Moderated.scala deleted file mode 100644 index b726c8932..000000000 --- a/MavLift/src/main/scala/code/model/traits/Moderated.scala +++ /dev/null @@ -1,233 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.traits -import java.util.Date -import net.liftweb.json.JsonAST.JObject -import net.liftweb.json.JsonAST.JString -import net.liftweb.json.JsonAST.JField -import net.liftweb.json._ -import net.liftweb.json.JsonDSL._ -import net.liftweb.http.JsonResponse -import net.liftweb.http.LiftResponse -import java.text.SimpleDateFormat - -class ModeratedOtherBankAccount (filteredId : String, filteredLabel : AccountName, - filteredNationalIdentifier : Option[String], filteredSWIFT_BIC : Option[Option[String]], - filteredIBAN : Option[Option[String]], filteredBankName: Option[String], - filteredNumber: Option[String], filteredMetadata : Option[ModeratedOtherBankAccountMetadata]) -{ - def id = filteredId - def label = filteredLabel - def nationalIdentifier = filteredNationalIdentifier - def swift_bic = filteredSWIFT_BIC - def iban = filteredIBAN - def bankName = filteredBankName - def number = filteredNumber - def metadata = filteredMetadata - def isAlias = filteredLabel.aliasType match{ - case Public | Private => true - case _ => false - } -} - -object ModeratedOtherBankAccount { - implicit def moderatedOtherBankAccount2Json(mOtherBank: ModeratedOtherBankAccount) : JObject = { - val holderName = mOtherBank.label.display - val isAlias = if(mOtherBank.isAlias) "yes" else "no" - val number = mOtherBank.number getOrElse "" - val kind = "" - val bankIBAN = (for { //TODO: This should be handled a bit better... might want to revisit the Option stuff in ModeratedOtherAccount etc. - i <- mOtherBank.iban - iString <- i - } yield iString).getOrElse("") - val bankNatIdent = mOtherBank.nationalIdentifier getOrElse "" - val bankName = mOtherBank.bankName getOrElse "" - ModeratedBankAccount.bankJson(holderName, isAlias, number, kind, bankIBAN, bankNatIdent, bankName) - } -} - -class ModeratedOtherBankAccountMetadata(filteredMoreInfo : Option[String], - filteredUrl : Option[String], filteredImageUrl : Option[String], filteredOpenCorporatesUrl : Option[String]) { - def moreInfo = filteredMoreInfo - def url = filteredUrl - def imageUrl = filteredImageUrl - def openCorporatesUrl = filteredOpenCorporatesUrl -} - -object ModeratedOtherBankAccountMetadata { - implicit def moderatedOtherBankAccountMetadata2Json(mOtherBankMeta: ModeratedOtherBankAccountMetadata) : JObject = { - JObject(JField("blah", JString("test")) :: Nil) - } -} - - -class ModeratedTransaction(filteredId: String, filteredBankAccount: Option[ModeratedBankAccount], - filteredOtherBankAccount: Option[ModeratedOtherBankAccount], filteredMetaData : Option[ModeratedTransactionMetadata], - filteredTransactionType: Option[String], filteredAmount: Option[BigDecimal], filteredCurrency: Option[String], - filteredLabel: Option[Option[String]],filteredStartDate: Option[Date], filteredFinishDate: Option[Date], - filteredBalance : String) { - - //the filteredBlance type in this class is a string rather than Big decimal like in Transaction trait for snippet (display) reasons. - //the view should be able to return a sign (- or +) or the real value. casting signs into bigdecimal is not possible - def id = filteredId - def bankAccount = filteredBankAccount - def otherBankAccount = filteredOtherBankAccount - def metadata = filteredMetaData - def transactionType = filteredTransactionType - def amount = filteredAmount - def currency = filteredCurrency - def label = filteredLabel - def startDate = filteredStartDate - def finishDate = filteredFinishDate - def balance = filteredBalance - - def dateOption2JString(date: Option[Date]) : JString = { - JString(date.map(d => ModeratedTransaction.dateFormat.format(d)) getOrElse "") - } - - def toJson(view: View): JObject = { - ("view" -> view.permalink) ~ - ("uuid" -> id) ~ - ("this_account" -> bankAccount) ~ - ("other_account" -> otherBankAccount) ~ - ("details" -> - ("type_en" -> transactionType) ~ //TODO: Need translations for transaction types and a way to - ("type_de" -> transactionType) ~ // figure out what language the original type is in - ("posted" -> dateOption2JString(startDate)) ~ - ("completed" -> dateOption2JString(finishDate)) ~ - ("new_balance" -> - ("currency" -> currency.getOrElse("")) ~ //TODO: Need separate currency for balances and values? - ("amount" -> balance)) ~ - ("value" -> - ("currency" -> currency.getOrElse("")) ~ - ("amount" -> amount))) - } -} - -object ModeratedTransaction { - val dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") -} - -class ModeratedTransactionMetadata(filteredOwnerComment : Option[String], filteredComments : Option[List[Comment]], - addOwnerComment : Option[(String => Unit)], addCommentFunc: Option[(Long, Long, String, Date) => Unit]) - { - def ownerComment = filteredOwnerComment - def comments = filteredComments - def ownerComment(text : String) = addOwnerComment match { - case None => None - case Some(o) => o.apply(text) - } - def addComment= addCommentFunc - - } - -object ModeratedTransactionMetadata { - implicit def moderatedTransactionMetadata2Json(mTransactionMeta: ModeratedTransactionMetadata) : JObject = { - JObject(JField("blah", JString("test")) :: Nil) - } -} - -class ModeratedBankAccount(filteredId : String, - filteredOwners : Option[Set[AccountOwner]], filteredAccountType : Option[String], - filteredBalance: String, filteredCurrency : Option[String], - filteredLabel : Option[String], filteredNationalIdentifier : Option[String], - filteredSwift_bic : Option[Option[String]], filteredIban : Option[Option[String]], - filteredNumber: Option[String], filteredBankName: Option[String]) -{ - def id = filteredId - def owners = filteredOwners - def accountType = filteredAccountType - def balance = filteredBalance - def currency = filteredCurrency - def label = filteredLabel - def nationalIdentifier = filteredNationalIdentifier - def swift_bic = filteredSwift_bic - def iban = filteredIban - def number = filteredNumber - def bankName = filteredBankName - - def toJson = { - //TODO: Decide if unauthorised info (I guess that is represented by a 'none' option'? I can't really remember) - // should just disappear from the json or if an empty string should be used. - //I think we decided to use empty strings. What was the point of all the options again? - ("number" -> number.getOrElse("")) ~ - ("owners" -> owners.flatten.map(owner => - ("id" ->owner.id) ~ - ("name" -> owner.name))) ~ - ("type" -> accountType.getOrElse("")) ~ - ("balance" -> - ("currency" -> currency.getOrElse("")) ~ - ("amount" -> balance)) ~ - ("IBAN" -> iban.getOrElse(Some(""))) ~ - ("date_opened" -> "") - } -} - -object ModeratedBankAccount { - - def bankJson(holderName: String, isAlias : String, number: String, - kind: String, bankIBAN: String, bankNatIdent: String, - bankName: String) : JObject = { - ("holder" -> - ( - ("name" -> holderName) ~ - ("alias"-> isAlias) - ))~ - ("number" -> number) ~ - ("kind" -> kind) ~ - ("bank" -> - ("IBAN" -> bankIBAN) ~ - ("national_identifier" -> bankNatIdent) ~ - ("name" -> bankName)) - } - - implicit def moderatedBankAccount2Json(mBankAccount: ModeratedBankAccount) : JObject = { - val holderName = mBankAccount.owners match{ - case Some(ownersSet) => if(ownersSet.size!=0) - ownersSet.toList(0).name - else - "" - case _ => "" - } - val isAlias = "no" - val number = mBankAccount.number getOrElse "" - val kind = mBankAccount.accountType getOrElse "" - val bankIBAN = (for { //TODO: This should be handled a bit better... might want to revisit the Option stuff in ModeratedOtherAccount etc. - i <- mBankAccount.iban - iString <- i - } yield iString).getOrElse("") - val bankNatIdent = mBankAccount.nationalIdentifier getOrElse "" - val bankName = mBankAccount.bankName getOrElse "" - bankJson(holderName, isAlias, number, kind, bankIBAN, bankNatIdent, bankName) - } -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/traits/Transaction.scala b/MavLift/src/main/scala/code/model/traits/Transaction.scala deleted file mode 100644 index b35b52fdb..000000000 --- a/MavLift/src/main/scala/code/model/traits/Transaction.scala +++ /dev/null @@ -1,69 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.traits -import scala.math.BigDecimal -import java.util.Date - -trait Transaction { - - def id : String - - var thisAccount : BankAccount - - def otherAccount : OtherBankAccount - - def metadata : TransactionMetadata - - //E.g. cash withdrawal, electronic payment, etc. - def transactionType : String - - //TODO: Check if BigDecimal is an appropriate data type - def amount : BigDecimal - - //ISO 4217, e.g. EUR, GBP, USD, etc. - def currency : String - - // Bank provided comment - def label : Option[String] - - // The date the transaction was initiated - def startDate : Date - - // The date when the money finished changing hands - def finishDate : Date - - //the new balance for the bank account - //TODO : Rethink this - def balance : BigDecimal - -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/model/traits/View.scala b/MavLift/src/main/scala/code/model/traits/View.scala deleted file mode 100644 index 99d399696..000000000 --- a/MavLift/src/main/scala/code/model/traits/View.scala +++ /dev/null @@ -1,421 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.model.traits -import code.snippet.CustomEditable -import net.liftweb.http.SHtml -import net.liftweb.json.JsonDSL._ -import net.liftweb.json.JsonAST.JObject -import net.liftweb.common.Box -import net.liftweb.common.Empty -import net.liftweb.common.Full - -class AliasType -class Alias extends AliasType -object Public extends Alias -object Private extends Alias -object NoAlias extends AliasType -case class AccountName(display: String, aliasType: AliasType) - -trait View { - - //e.g. "Anonymous", "Authorities", "Our Network", etc. - def id: Long - def name: String - def description : String - def permalink : String - - //the view settings - def usePrivateAliasIfOneExists: Boolean - def usePublicAliasIfOneExists: Boolean - - //reading access - - //transaction fields - def canSeeTransactionThisBankAccount : Boolean - def canSeeTransactionOtherBankAccount : Boolean - def canSeeTransactionMetadata : Boolean - def canSeeTransactionLabel: Boolean - def canSeeTransactionAmount: Boolean - def canSeeTransactionType: Boolean - def canSeeTransactionCurrency: Boolean - def canSeeTransactionStartDate: Boolean - def canSeeTransactionFinishDate: Boolean - def canSeeTransactionBalance: Boolean - - //transaction metadata - def canSeeComments: Boolean - def canSeeOwnerComment: Boolean - - //Bank account fields - def canSeeBankAccountOwners : Boolean - def canSeeBankAccountType : Boolean - def canSeeBankAccountBalance : Boolean - def canSeeBankAccountBalancePositiveOrNegative : Boolean - def canSeeBankAccountCurrency : Boolean - def canSeeBankAccountLabel : Boolean - def canSeeBankAccountNationalIdentifier : Boolean - def canSeeBankAccountSwift_bic : Boolean - def canSeeBankAccountIban : Boolean - def canSeeBankAccountNumber : Boolean - def canSeeBankAccountName : Boolean - - //other bank account fields - def canSeeOtherAccountNationalIdentifier : Boolean - def canSeeSWIFT_BIC : Boolean - def canSeeOtherAccountIBAN : Boolean - def canSeeOtherAccountBankName : Boolean - def canSeeOtherAccountNumber : Boolean - def canSeeOtherAccountMetadata :Boolean - - //other bank account meta data - def canSeeMoreInfo: Boolean - def canSeeUrl: Boolean - def canSeeImageUrl: Boolean - def canSeeOpenCorporatesUrl: Boolean - - //writing access - def canEditOwnerComment: Boolean - def canAddComments : Boolean - - // In the future we can add a method here to allow someone to show only transactions over a certain limit - - def moderate(transaction: Transaction): ModeratedTransaction = { - //transaction data - val transactionId = transaction.id - - val thisBankAccount = - if(canSeeTransactionThisBankAccount) - { - val owners = if(canSeeBankAccountOwners) Some(transaction.thisAccount.owners) else None - val accountType = if(canSeeBankAccountType) Some(transaction.thisAccount.accountType) else None - val balance = if(canSeeBankAccountBalance) { - transaction.thisAccount.balance.toString - } else if (canSeeBankAccountBalancePositiveOrNegative) { - if(transaction.thisAccount.balance.toString.startsWith("-")) "-" else "+" - } else "" - val currency = if(canSeeBankAccountCurrency) Some(transaction.thisAccount.currency) else None - val label = if(canSeeBankAccountLabel) Some(transaction.thisAccount.label) else None - val number = if(canSeeBankAccountNumber) Some(transaction.thisAccount.number) else None - val bankName = if(canSeeBankAccountName) Some(transaction.thisAccount.bankName) else None - val nationalIdentifier = - if(canSeeBankAccountNationalIdentifier) - Some(transaction.thisAccount.nationalIdentifier) - else - None - val swift_bic = - if(canSeeBankAccountSwift_bic) - Some(transaction.thisAccount.swift_bic) - else - None - val iban = - if(canSeeBankAccountIban) - Some(transaction.thisAccount.iban) - else - None - Some(new ModeratedBankAccount(transaction.thisAccount.id, owners, accountType, balance, currency, label, - nationalIdentifier, swift_bic, iban, number, bankName)) - } - else - None - - val otherBankAccount = - if (canSeeTransactionOtherBankAccount) - { - //other account data - var otherAccountId = transaction.otherAccount.id - val otherAccountLabel: AccountName = - { - val realName = transaction.otherAccount.label - if (usePublicAliasIfOneExists) { - - val publicAlias = transaction.otherAccount.metadata.publicAlias - - if (! publicAlias.isEmpty ) AccountName(publicAlias, Public) - else AccountName(realName, NoAlias) - - } else if (usePrivateAliasIfOneExists) { - - val privateAlias = transaction.otherAccount.metadata.privateAlias - - if (! privateAlias.isEmpty) AccountName(privateAlias, Private) - else AccountName(realName, Private) - } else - AccountName(realName, NoAlias) - } - val otherAccountNationalIdentifier = if (canSeeOtherAccountNationalIdentifier) Some(transaction.otherAccount.nationalIdentifier) else None - val otherAccountSWIFT_BIC = if (canSeeSWIFT_BIC) Some(transaction.otherAccount.swift_bic) else None - val otherAccountIBAN = if(canSeeOtherAccountIBAN) Some(transaction.otherAccount.iban) else None - val otherAccountBankName = if(canSeeOtherAccountBankName) Some(transaction.otherAccount.bankName) else None - val otherAccountNumber = if(canSeeOtherAccountNumber) Some(transaction.otherAccount.number) else None - val otherAccountMetadata = - if(canSeeOtherAccountMetadata) - { - //other bank account metadata - val moreInfo = - if (canSeeMoreInfo) Some(transaction.otherAccount.metadata.moreInfo) - else None - val url = - if (canSeeUrl) Some(transaction.otherAccount.metadata.url) - else None - val imageUrl = - if (canSeeImageUrl) Some(transaction.otherAccount.metadata.imageUrl) - else None - val openCorporatesUrl = - if (canSeeOpenCorporatesUrl) Some(transaction.otherAccount.metadata.openCorporatesUrl) - else None - - Some(new ModeratedOtherBankAccountMetadata(moreInfo, url, imageUrl, openCorporatesUrl)) - } - else - None - - Some(new ModeratedOtherBankAccount(otherAccountId,otherAccountLabel, otherAccountNationalIdentifier, - otherAccountSWIFT_BIC, otherAccountIBAN, otherAccountBankName, otherAccountNumber, otherAccountMetadata)) - } - else - None - - //transation metadata - val transactionMetadata = - if(canSeeTransactionMetadata) - { - val ownerComment = if (canSeeOwnerComment) transaction.metadata.ownerComment else None - val comments = - if (canSeeComments) - Some(transaction.metadata.comments.filter(comment => comment.viewId==id)) - else None - val addCommentFunc= if(canAddComments) Some(transaction.metadata.addComment _) else None - val addOwnerCommentFunc:Option[String=> Unit] = if (canEditOwnerComment) Some(transaction.metadata.ownerComment _) else None - new Some(new ModeratedTransactionMetadata(ownerComment,comments,addOwnerCommentFunc,addCommentFunc)) - } - else - None - - val transactionType = - if (canSeeTransactionType) Some(transaction.transactionType) - else None - - val transactionAmount = - if (canSeeTransactionAmount) Some(transaction.amount) - else None - - val transactionCurrency = - if (canSeeTransactionCurrency) Some(transaction.currency) - else None - - val transactionLabel = - if (canSeeTransactionLabel) Some(transaction.label) - else None - - val transactionStartDate = - if (canSeeTransactionStartDate) Some(transaction.startDate) - else None - - val transactionFinishDate = - if (canSeeTransactionFinishDate) Some(transaction.finishDate) - else None - - val transactionBalance = - if (canSeeTransactionBalance) transaction.balance.toString() - else "" - - new ModeratedTransaction(transactionId, thisBankAccount, otherBankAccount, transactionMetadata, - transactionType, transactionAmount, transactionCurrency, transactionLabel, transactionStartDate, - transactionFinishDate, transactionBalance) - } - - def moderate(bankAccount: BankAccount) : Box[ModeratedBankAccount] = { - if(bankAccount.allowAnnoymousAccess) { - val owners : Set[AccountOwner] = if(canSeeBankAccountOwners) bankAccount.owners else Set() - val balance = if(canSeeBankAccountBalance){ - bankAccount.balance.toString - } else if(canSeeBankAccountBalancePositiveOrNegative) { - if(bankAccount.balance.toString.startsWith("-")) "-" else "+" - } else "" - val accountType = if(canSeeBankAccountType) Some(bankAccount.accountType) else None - val currency = if(canSeeBankAccountCurrency) Some(bankAccount.currency) else None - val label = if(canSeeBankAccountLabel) Some(bankAccount.label) else None - val nationalIdentifier = if(canSeeBankAccountNationalIdentifier) Some(bankAccount.label) else None - val swiftBic = if(canSeeBankAccountSwift_bic) Some(bankAccount.swift_bic) else None - val iban = if(canSeeBankAccountIban) Some(bankAccount.iban) else None - val number = if(canSeeBankAccountNumber) Some(bankAccount.number) else None - val bankName = if(canSeeBankAccountName) Some(bankAccount.bankName) else None - - Full(new ModeratedBankAccount(filteredId = bankAccount.id, - filteredOwners = Some(owners), - filteredAccountType = accountType, - filteredBalance = balance, - filteredCurrency = currency, - filteredLabel = label, - filteredNationalIdentifier = nationalIdentifier, - filteredSwift_bic = swiftBic, - filteredIban = iban, - filteredNumber = number, - filteredBankName = bankName - )) - } - else Empty - } - - def toJson : JObject = { - ("name" -> name) ~ - ("description" -> description) - } - -} - -//An implementation that has the least amount of permissions possible -class BaseView extends View { - def id = 1 - def name = "Restricted" - def permalink = "restricted" - def description = "" - - //the view settings - def usePrivateAliasIfOneExists = true - def usePublicAliasIfOneExists = true - - //reading access - - //transaction fields - def canSeeTransactionThisBankAccount = false - def canSeeTransactionOtherBankAccount = false - def canSeeTransactionMetadata = false - def canSeeTransactionLabel = false - def canSeeTransactionAmount = false - def canSeeTransactionType = false - def canSeeTransactionCurrency = false - def canSeeTransactionStartDate = false - def canSeeTransactionFinishDate = false - def canSeeTransactionBalance = false - - //transaction metadata - def canSeeComments = false - def canSeeOwnerComment = false - - //Bank account fields - def canSeeBankAccountOwners = false - def canSeeBankAccountType = false - def canSeeBankAccountBalance = false - def canSeeBankAccountBalancePositiveOrNegative = false - def canSeeBankAccountCurrency = false - def canSeeBankAccountLabel = false - def canSeeBankAccountNationalIdentifier = false - def canSeeBankAccountSwift_bic = false - def canSeeBankAccountIban = false - def canSeeBankAccountNumber = false - def canSeeBankAccountName = false - - //other bank account fields - def canSeeOtherAccountNationalIdentifier = false - def canSeeSWIFT_BIC = false - def canSeeOtherAccountIBAN = false - def canSeeOtherAccountBankName = false - def canSeeOtherAccountNumber = false - def canSeeOtherAccountMetadata = false - - //other bank account meta data - def canSeeMoreInfo = false - def canSeeUrl = false - def canSeeImageUrl = false - def canSeeOpenCorporatesUrl = false - - //writing access - def canEditOwnerComment = false - def canAddComments = false - -} - -class FullView extends View { - def id = 2 - def name = "Full" - def permalink ="full" - def description = "" - - //the view settings - def usePrivateAliasIfOneExists = false - def usePublicAliasIfOneExists = false - - //reading access - - //transaction fields - def canSeeTransactionThisBankAccount = true - def canSeeTransactionOtherBankAccount = true - def canSeeTransactionMetadata = true - def canSeeTransactionLabel = true - def canSeeTransactionAmount = true - def canSeeTransactionType = true - def canSeeTransactionCurrency = true - def canSeeTransactionStartDate = true - def canSeeTransactionFinishDate = true - def canSeeTransactionBalance = true - - //transaction metadata - def canSeeComments = true - def canSeeOwnerComment = true - - //Bank account fields - def canSeeBankAccountOwners = true - def canSeeBankAccountType = true - def canSeeBankAccountBalance = true - def canSeeBankAccountBalancePositiveOrNegative = true - def canSeeBankAccountCurrency = true - def canSeeBankAccountLabel = true - def canSeeBankAccountNationalIdentifier = true - def canSeeBankAccountSwift_bic = true - def canSeeBankAccountIban = true - def canSeeBankAccountNumber = true - def canSeeBankAccountName = true - - //other bank account fields - def canSeeOtherAccountNationalIdentifier = true - def canSeeSWIFT_BIC = true - def canSeeOtherAccountIBAN = true - def canSeeOtherAccountMetadata = true - def canSeeOtherAccountBankName = true - def canSeeOtherAccountNumber = true - - //other bank account meta data - def canSeeMoreInfo = true - def canSeeUrl = true - def canSeeImageUrl = true - def canSeeOpenCorporatesUrl = true - - //writing access - def canEditOwnerComment = true - def canAddComments = true - -} - - diff --git a/MavLift/src/main/scala/code/snippet/AccountRegistration.scala b/MavLift/src/main/scala/code/snippet/AccountRegistration.scala new file mode 100644 index 000000000..8d0a415c0 --- /dev/null +++ b/MavLift/src/main/scala/code/snippet/AccountRegistration.scala @@ -0,0 +1,189 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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.snippet + +import code.model.dataAccess.{OBPUser,HostedBank} +import net.liftweb.common.{Full,Box,Empty,Failure} +import scala.xml.NodeSeq +import net.liftweb.util.CssSel +import net.liftweb.util.Helpers._ +import net.liftweb.util.Props +import net.liftweb.http.{S,SHtml,RequestVar} +import code.pgp.PgpEncryption +import net.liftweb.http.js.JsCmd +import net.liftweb.http.js.JsCmds.Noop +import scala.xml.Text +import net.liftweb.util.Mailer +import Mailer._ +import net.liftweb.common.Loggable + +class AccountRegistration extends Loggable { + + private object bankName extends RequestVar("") + private object accountNumber extends RequestVar("") + private object accountPIN extends RequestVar("") + private object publicAccess extends RequestVar(false) + private object accountHolder extends RequestVar("") + private object accountKind extends RequestVar("") + private object accountLabel extends RequestVar("") + private object accountName extends RequestVar("") + + def renderForm = { + OBPUser.currentUser match { + case Full(user) => { + //load the suported banks list from the database + val banks = "Choose a Bank" :: HostedBank.findAll.map(_.name.get) + val options = Map("yes" -> true,"no" -> false) + val optionsSwaped = options.map{_.swap} + + //get a boolean value from a 'yes' or 'no' string + def getBooleanValue(text : Box[String]) = + text match { + case Full(value) => tryo{ + options(value) + } match { + case Full(boolean) => boolean + case _ => false + } + case _ => false + } + + def check() = + //check that all the parameters are here + if( !accountNumber.is.isEmpty && !accountPIN.is.isEmpty & + !accountName.is.isEmpty && !accountHolder.is.isEmpty & + !accountKind.is.isEmpty && ! accountLabel.is.isEmpty & + bankName.is != "Choose a Bank" ) + { + var reponceText = "Submission Failed. Please try later." + var reponceId = "submissionFailed" + val fileName = bankName.is+"-"+accountNumber.is+"-"+user.emailAddress + for{ + //load the public key and output directory path + publicKey <- Props.get("publicKeyPath") + outputFilesDirectory <- Props.get("outputFilesDirectory") + }yield tryo{ + //store the Pin code into an encrypted file + PgpEncryption.encryptToFile( + accountPIN.is, + publicKey, + outputFilesDirectory+"/"+fileName+".pin") + } match { + case Full(encryptedPin) => { + //send an email to the administration so we can setup the account + //prepare the data to be sent into the email body + val emailBody = + "The following account needs to activated : " +"\n"+ + "bank name : " + bankName.is +"\n"+ + "account number : " + accountNumber.is +"\n"+ + "account name : " + accountName.is +"\n"+ + "account holder : " + accountHolder.is +"\n"+ + "account label : " + accountLabel.is +"\n"+ + "account kind : " + accountKind.is +"\n"+ + "public view : " + publicAccess.is.toString +"\n"+ + "user email : " + user.emailAddress +"\n"+ + "The PIN code is in this file : "+ outputFilesDirectory+"/"+fileName+".pin"+"\n" + val accountNotificationemails = Props.get("accountNotificationemails") match { + case Full(emails) => emails.split(",",0) + case _ => Array() + } + + tryo { + accountNotificationemails.foreach ( email => + Mailer.sendMail(From("noreply@openbankproject.com"),Subject("[SoFi]Bank account Activation"), + To(email),PlainMailBodyType(emailBody)) + ) + } match { + case Failure(message, exception, chain) => + logger.error("problem while sending email: " + message) + case _ => + logger.info("successfully sent email") + } + reponceText = "Submission succeded. You will receive an email notification once the bank account will be setup by the administrator." + reponceId = "submissionSuccess" + } + case _ => //nothing to do the text and Id are allready set + } + //set the fields to their default values + bankName.set("Choose a Bank") + accountNumber.set("") + accountPIN.set("") + publicAccess.set(false) + accountHolder.set("") + accountKind.set("") + accountLabel.set("") + accountName.set("") + //return a message + S.notice("submissionMessage", SHtml.span(Text(reponceText), Noop,("id",reponceId))) + } + else + { + if(bankName.is == "Choose a Bank") + S.error("bankError","Bank not selected ! ") + if(accountNumber.is.isEmpty) + S.error("accountNumberError","Account Number Empty ! ") + if(accountPIN.is.isEmpty) + S.error("accountPINError","Account PIN Empty ! ") + if(accountHolder.is.isEmpty) + S.error("accountHolderError","Account Holder Empty ! ") + if(accountKind.is.isEmpty) + S.error("accountKindError","Account Kind Empty ! ") + if(accountLabel.is.isEmpty) + S.error("accountLabelError","Account label Empty ! ") + if(accountName.is.isEmpty) + S.error("accountNameError","Account Name Empty ! ") + } + + //now we create the form fields + "#bankListCol" #> SHtml.selectElem(banks,Full(bankName.is),("id","bankList"))((v : String) => bankName.set(v)) & + "#accountNumberCol" #> SHtml.textElem(accountNumber,("id","accountNumber"),("placeholder","123456")) & + "#accountPINCol" #> SHtml.passwordElem(accountPIN,("id","accountPIN"),("placeholder","*******")) & + "#publicViewCol" #> SHtml.radioElem(options.keys.toList,Full(optionsSwaped(publicAccess.is)))((v : Box[String]) => publicAccess.set(getBooleanValue(v))).toForm & + "#accountHolderCol" #> SHtml.textElem(accountHolder,("id","accountHolder"),("placeholder","John Doe")) & + "#accountKindCol" #> SHtml.textElem(accountKind,("id","accountKind"),("placeholder","saving, current, ...")) & + "#accountLabelCol" #> SHtml.textElem(accountLabel,("id","accountLabel"),("placeholder","John main buisness account,...")) & + "#accountNameCol" #> SHtml.textElem(accountName,("id","accountName"),("placeholder","mycompany, personal, etc")) & + "type=submit" #> SHtml.onSubmitUnit(check) & + "#loginMsg" #> NodeSeq.Empty + } + case _ => + //if the user is not logged in, we hide the form and ask him to login + "#submitAccount" #> NodeSeq.Empty & + "#loginMsg * " #> { + Text("You must ") ++ + SHtml.link(OBPUser.loginPageURL,() => {},Text("login"),("title","signup/login")) ++ + Text(" before you can connect your bank account! ") + } + } + } +} diff --git a/MavLift/src/main/scala/code/snippet/ConsumerRegistration.scala b/MavLift/src/main/scala/code/snippet/ConsumerRegistration.scala new file mode 100644 index 000000000..e04d2ae4f --- /dev/null +++ b/MavLift/src/main/scala/code/snippet/ConsumerRegistration.scala @@ -0,0 +1,126 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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.snippet + +import net.liftweb.util.Helpers._ +import code.model.Consumer +import net.liftweb.http.S +import net.liftweb.common.Full +import net.liftweb.http.SHtml +import net.liftweb.http.RequestVar +import net.liftweb.util.FieldError +import net.liftweb.util.Helpers + +class ConsumerRegistration { + + val NOOP_SELECTOR = "#i_am_an_id_that_should_never_exist" #> "" + + private object nameVar extends RequestVar("") + private object appTypeVar extends RequestVar[Consumer.appType.enum.AppType](Consumer.appType.enum.values.head) + private object descriptionVar extends RequestVar("") + private object devEmailVar extends RequestVar("") + + def registerForm = { + + def registerWithoutWarnings = + register & + ".registration-error" #> "" + + def register = { + ".register" #> { + ".app-type-option" #> { + val appTypes = Consumer.appType.enum.values.map(appType => appType.toString) + appTypes.map(t => { + val selected = appTypeVar.get.toString == t + + def markIfSelected = + if(selected) "* [selected]" #> "selected" + else NOOP_SELECTOR + + markIfSelected & + "* *" #> t & + "* [value]" #> t + }) + } & + "name=app-name [value]" #> nameVar.get & + "name=app-description *" #> descriptionVar.get & + "name=app-developer [value]" #> devEmailVar.get + } & + ".success" #> "" + } + + def showResults(consumer : Consumer) = { + //thanks for registering, here's your key, etc. + ".app-name *" #> consumer.name.get & + ".app-type *" #> consumer.appType.get.toString & + ".app-description *" #> consumer.description.get & + ".app-developer *" #> consumer.developerEmail.get & + ".auth-key *" #> consumer.key.get & + ".secret-key *" #> consumer.secret.get & + ".registration" #> "" + } + + def saveAndShowResults(consumer : Consumer) = { + consumer.isActive(true).key(Helpers.randomString(40).toLowerCase).secret(Helpers.randomString(40).toLowerCase).save + showResults(consumer) + } + + def showErrors(errors : List[FieldError]) = { + register & + ".registration-error *" #> errors.map(_.msg.toString).mkString(", ") + } + + def analyseResult = { + val name = S.param("app-name") getOrElse "" + val appType = S.param("app-type").flatMap(typeString => Consumer.appType.enum.values.find(p => p.toString == typeString)) getOrElse + Consumer.appType.enum.values.head + val appDescription = S.param("app-description") getOrElse "" + val developerEmail = S.param("app-developer") getOrElse "" + + val consumer = Consumer.create.name(name).appType(appType).description(appDescription).developerEmail(developerEmail) + + val errors = consumer.validate + nameVar.set(name) + appTypeVar.set(appType) + descriptionVar.set(appDescription) + devEmailVar.set(developerEmail) + + if(errors.size == 0) saveAndShowResults(consumer) + else showErrors(errors) + } + + if(S.post_?) analyseResult + else register + + } + +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/snippet/CustomEditable.scala b/MavLift/src/main/scala/code/snippet/CustomEditable.scala deleted file mode 100644 index 874f343bc..000000000 --- a/MavLift/src/main/scala/code/snippet/CustomEditable.scala +++ /dev/null @@ -1,105 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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.snippet -import net.liftweb.http.SHtml -import scala.xml.NodeSeq -import net.liftweb.http.js.JsCmd -import scala.xml.Text - -object CustomEditable { - - //Borrows very heavily from SHtml.ajaxEditable - //TODO: This should go. There is too much presentation stuff living here in the code - def editable(label : => String, editForm: => NodeSeq, onSubmit: () => JsCmd, defaultValue: String): NodeSeq = { - import net.liftweb.http.js - import net.liftweb.http.S - import js.{ jquery, JsCmd, JsCmds, JE } - import jquery.JqJsCmds - import JsCmds.{ Noop, SetHtml } - import JE.Str - import JqJsCmds.{ Hide, Show } - import net.liftweb.util.Helpers - - val divName = Helpers.nextFuncName - val dispName = divName + "_display" - val editName = divName + "_edit" - - def swapJsCmd(show: String, hide: String): JsCmd = Show(show) & Hide(hide) - - def setAndSwap(show: String, showContents: => NodeSeq, hide: String): JsCmd = - (SHtml.ajaxCall(Str("ignore"), { ignore: String => SetHtml(show, showContents) })._2.cmd & swapJsCmd(show, hide)) - - val editClass = "edit" - val addClass = "add" - def aClass = if (label.equals("")) addClass else editClass - def displayText = if (label.equals("")) defaultValue else label - - def displayMarkup: NodeSeq = { - label match { - case "" => { - - } - case _ => { - - } - } - } - - def editMarkup: NodeSeq = { - val formData: NodeSeq = - editForm ++
- ++ - SHtml.hidden(onSubmit, ("float", "left")) ++ - - - SHtml.ajaxForm(formData, - Noop, - setAndSwap(dispName, displayMarkup, editName)) - } - -
-
- { displayMarkup } -
- -
- } - -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/snippet/Nav.scala b/MavLift/src/main/scala/code/snippet/Nav.scala new file mode 100644 index 000000000..d05c20ab7 --- /dev/null +++ b/MavLift/src/main/scala/code/snippet/Nav.scala @@ -0,0 +1,221 @@ +/** +Open Bank Project - Transparency / Social Finance Web Application +Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . + +Email: contact@tesobe.com +TESOBE / Music Pictures 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.snippet +import scala.xml.NodeSeq +import net.liftweb.http.S +import net.liftweb.http.LiftRules +import net.liftweb.util.Helpers +import net.liftweb.util.Helpers._ +import scala.xml.Group +import net.liftweb.sitemap.Loc +import net.liftweb.common.Box +import net.liftweb.common.Full +import net.liftweb.common.Empty +import net.liftweb.sitemap.SiteMapSingleton +import code.model.dataAccess.{OBPUser,Account, LocalStorage} +import net.liftweb.http.SHtml +import net.liftweb.http.js.JsCmd +import net.liftweb.http.js.JsCmds._Noop +import code.model.BankAccount + +class Nav { + + def eraseMenu = + "* * " #> "" + def views :net.liftweb.util.CssSel = { + val url = S.uri.split("/",0) + if(url.size>4) + OBPUser.currentUser match { + case Full(user) => { + val bankAccount = BankAccount(url(2), url(4)) + val viewsListBox = for { + b <- bankAccount + } yield user.permittedViews(b) + val viewsList = viewsListBox getOrElse Nil + if(viewsList.size>0) + ".navitem *" #> { + viewsList.toList.map(view => { + val viewUrl = "/banks/"+url(2)+"/accounts/"+url(4)+"/"+view.permalink + ".navlink [href]" #> {viewUrl} & + ".navlink *" #> view.name & + ".navlink [class+]" #> markIfSelected(viewUrl) + })} + else + eraseMenu + } + case _ => LocalStorage.getAccount(url(2), url(4)) match { + case Full(account) => if(account.anonAccess.is) + ".navitem *" #> { + val anoymousUrl = "/banks/"+url(2)+"/accounts/"+url(4)+"/public" + ".navlink [href]" #> {anoymousUrl} & + ".navlink *" #> "Public" & + ".navlink [class+]" #> markIfSelected(anoymousUrl) + } + else + eraseMenu + case _ => eraseMenu + } + + } + else + eraseMenu + } + + def management = { + val url = S.uri.split("/", 0) + + def getManagement = for { + user <- OBPUser.currentUser + bankAccount <- BankAccount(url(2), url(4)) + if (user.hasMangementAccess(bankAccount)) + } yield { + val managementUrl = "/banks/" + url(2) + "/accounts/" + url(4) + "/management" + ".navlink [href]" #> { managementUrl } & + ".navlink *" #> "Management" & + ".navlink [class+]" #> markIfSelected(managementUrl) + } + + if (url.size > 4) getManagement getOrElse eraseMenu + else eraseMenu + } + + def item = { + val attrs = S.prefixedAttrsToMetaData("a") + val name = S.attr("name").getOrElse("") + val loc = + for{ + sitemap <- LiftRules.siteMap + l <- new SiteMapSingleton().findAndTestLoc(name) + } yield l + + ".navitem *" #>{ + loc.map(navItemSelector) + } + } + + def navItemSelector(l : Loc[_]) = { + ".navlink [href]" #> l.calcDefaultHref & + ".navlink *" #> l.linkText & + ".navlink [class+]" #> markIfSelected(l.calcDefaultHref) + } + + def onlyOnSomePages = { + val pages : List[String]= S.attr("pages").map(_.toString.split(",").toList).getOrElse(Nil) + + val locs = pages.flatMap(page => (for{ + sitemap <- LiftRules.siteMap + l <- new SiteMapSingleton().findAndTestLoc(page) + } yield l)) + + val isPage = locs.map(l => { + //hack due to deadline to fix / and /index being the same + val currentPage = if(S.uri == "/") "/index" else S.uri + (l.calcDefaultHref == currentPage) + }).exists(_ == true) + + if(isPage) item + else "* *" #> "" + } + + def privilegeAdmin = { + val url = S.uri.split("/", 0) + + def hide = ".navitem *" #> "" + + def getPrivilegeAdmin = for { + bankAccount <- BankAccount(url(2), url(4)) + user <- OBPUser.currentUser + if (user.hasOwnerPermission(bankAccount)) + loc <- new SiteMapSingleton().findAndTestLoc("Privilege Admin") + } yield { + ".navitem *" #> { + ".navlink [href]" #> loc.calcDefaultHref & + ".navlink *" #> loc.linkText & + ".navlink [class+]" #> markIfSelected(loc.calcDefaultHref) + } + } + + if(url.size > 4) getPrivilegeAdmin.getOrElse(hide) else hide + } + + def markIfSelected(href : String) : Box[String]= { + val currentHref = S.uri + if(href.equals(currentHref)) Full("selected") + else Empty + } + + def listAccounts = { + var accounts : List[(String, String)] = List() + OBPUser.currentUser match { + case Full(user) => Account.findAll.map(account => { + val bankAccount = Account.toBankAccount(account) + if(user.permittedViews(bankAccount).size != 0) + accounts ::= (account.bankPermalink + "," + account.permalink, account.bankName + " - " + account.name) + }) + case _ => Account.findAll.map(account => + if(account.anonAccess.is) + accounts ::= (account.bankPermalink + "," + account.permalink, account.bankName + " - " + account.name) + ) + } + accounts ::= ("0","--> Choose an account") + def redirect(selectValue : String) : JsCmd = + { + val bankAndaccount = selectValue.split(",",0) + if(bankAndaccount.size==2) + if (LocalStorage.correctBankAndAccount(bankAndaccount(0), bankAndaccount(1))) + //TODO : the account may not has an public view, so this redirection would retun a 404 + //a better solution has to be found + S.redirectTo("/banks/" + bankAndaccount(0) + "/accounts/" + bankAndaccount(1) +"/public") + else + _Noop + else + _Noop + } + def computeDefaultValue : Box[String] = + { + val url = S.uri.split("/",0) + var output="0" + if(url.size>4) + output = url(2) + "," + url(4) + Full(output) + } + "#accountList *" #> { + computeDefaultValue match { + case Full("postbank,tesobe") => + SHtml.ajaxSelect(accounts,computeDefaultValue,redirect _) + case _ => + NodeSeq.Empty + } + } + } +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/snippet/OAuthAuthorisation.scala b/MavLift/src/main/scala/code/snippet/OAuthAuthorisation.scala new file mode 100644 index 000000000..5ef6aabc9 --- /dev/null +++ b/MavLift/src/main/scala/code/snippet/OAuthAuthorisation.scala @@ -0,0 +1,149 @@ + /** +Open Bank Project + +Copyright 2011,2012 TESOBE / Music Pictures Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + Open Bank Project (http://www.openbankproject.com) + Copyright 2011,2012 TESOBE / Music Pictures Ltd + + This product includes software developed at + TESOBE (http://www.tesobe.com/) + by + Simon Redfern : simon AT tesobe DOT com + Everett Sochowski: everett AT tesobe DOT com + Ayoub Benali : ayoub AT tesobe Dot com +*/ +package code.snippet +import net.liftweb.http.rest.RestHelper +import net.liftweb.http.Req +import net.liftweb.http.GetRequest +import net.liftweb.http.PostRequest +import net.liftweb.http.LiftResponse +import net.liftweb.common.Box +import net.liftweb.http.InMemoryResponse +import net.liftweb.common.{Full,Empty} +import net.liftweb.http.S +import code.model.{Nonce, Consumer, Token} +import net.liftweb.mapper.By +import java.util.Date +import java.net.{URLEncoder, URLDecoder} +import javax.crypto.spec.SecretKeySpec +import javax.crypto.Mac +import net.liftweb.util.Helpers +import code.model.AppType._ +import code.model.TokenType +import TokenType._ +import scala.compat.Platform +import code.model.dataAccess.OBPUser +import scala.xml.NodeSeq +import net.liftweb.util.Helpers._ + +object OAuthAuthorisation { + + // this method is specific to the authorization page ( where the user login to grant access + // to the application (step 2)) + def tokenCheck = + S.param("oauth_token") match { + case Full(token) => + Token.find(By(Token.key,Helpers.urlDecode(token.toString)),By(Token.tokenType,TokenType.Request)) match { + case Full(appToken) => + //check if the token is still valid + if(appToken.isValid) + if(OBPUser.loggedIn_?) + { + var verifier ="" + // if the user is logged in and no verifier have been generated + if(appToken.verifier.isEmpty) + { + val randomVerifier = appToken.gernerateVerifier + //the user is logged in so we have the current user + val user = OBPUser.currentUser.get + //FIXME: The whole snippet still use OBPUser, we must change it to the User trait + appToken.userId(user.id_) + if(appToken.save()) + verifier = randomVerifier + } + else + verifier=appToken.verifier + + // show the verifier if the application does not support + // redirection + if(appToken.callbackURL.is =="oob") + "#verify-code *" #> verifier & + "#errorMessage" #> "" & + "#account" #> "" + else + { + //redirect the user to the application with the verifier + S.redirectTo(appToken.callbackURL+"?oauth_token="+token+ + "&oauth_verifier="+verifier) + "#verifier" #> "you should be redirected" + } + } + else + //the user is not logged in so we show a login form + Consumer.find(By(Consumer.id,appToken.consumerId)) match { + case Full(consumer) => { + "#applicationName" #> consumer.name & + "#verifier" #>NodeSeq.Empty & + "#errorMessage" #> NodeSeq.Empty & + { + ".login [action]" #> OBPUser.loginPageURL & + ".forgot [href]" #> { + val href = for { + menu <- OBPUser.resetPasswordMenuLoc + } yield menu.loc.calcDefaultHref + + href getOrElse "#" + } & + ".signup [href]" #> + OBPUser.signUpPath.foldLeft("")(_ + "/" + _) + } + } + case _ => + "#errorMessage" #> "Application not found" & + "#userAccess" #> NodeSeq.Empty + } + else + "#errorMessage" #> "Token expired" & + "#userAccess" #> NodeSeq.Empty + case _ => + "#errorMessage" #> "This token does not exist" & + "#userAccess" #> NodeSeq.Empty + } + case _ => + "#errorMessage" #> "There is no Token"& + "#userAccess" #> NodeSeq.Empty + } + + //looks for expired tokens and nonces and delete them + def dataBaseCleaner : Unit = { + import net.liftweb.util.Schedule + import net.liftweb.mapper.By_< + Schedule.schedule(dataBaseCleaner _, 1 hour) + + val currentDate = new Date() + + /* + As in "wrong timestamp" function, 3 minutes is the timestamp limit where we accept + requests. So this function will delete nonces which have a timestamp older than + currentDate - 3 minutes + */ + val timeLimit = new Date(currentDate.getTime + 180000) + + //delete expired tokens and nonces + (Token.findAll(By_<(Token.expirationDate,currentDate)) ++ Nonce.findAll(By_<(Nonce.timestamp,timeLimit))).foreach(t => t.delete_!) + } +} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/snippet/OAuthHandshake.scala b/MavLift/src/main/scala/code/snippet/OAuthHandshake.scala deleted file mode 100644 index 222eec572..000000000 --- a/MavLift/src/main/scala/code/snippet/OAuthHandshake.scala +++ /dev/null @@ -1,552 +0,0 @@ -/** -Open Bank Project - -Copyright 2011,2012 TESOBE / Music Pictures Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - - Open Bank Project (http://www.openbankproject.com) - Copyright 2011,2012 TESOBE / Music Pictures Ltd - - This product includes software developed at - TESOBE (http://www.tesobe.com/) - by - Simon Redfern : simon AT tesobe DOT com - Everett Sochowski: everett AT tesobe DOT com - Ayoub Benali : ayoub AT tesobe Dot com - */ -package code.snippet -import net.liftweb.http.rest.RestHelper -import net.liftweb.http.Req -import net.liftweb.http.GetRequest -import net.liftweb.http.PostRequest -import net.liftweb.http.LiftResponse -import net.liftweb.common.Box -import net.liftweb.http.InMemoryResponse -import net.liftweb.common.{Full,Empty} -import net.liftweb.http.S -import code.model.{Nonce, Consumer, Token} -import net.liftweb.mapper.By -import java.util.Date -import java.net.{URLEncoder, URLDecoder} -import javax.crypto.spec.SecretKeySpec -import javax.crypto.Mac -import net.liftweb.util.Helpers -import code.model.AppType._ -import code.model.TokenType._ -import scala.compat.Platform -import code.model.dataAccess.OBPUser -import scala.xml.NodeSeq -import net.liftweb.util.Helpers._ - -object OAuthHandshake extends RestHelper -{ - serve - { - //Handling get request for a "request token" - case Req("oauth" :: "initiate" :: Nil,_ ,PostRequest) => - { - //Extract the OAuth parameters from the header and test if the request is valid - var (httpCode, data, oAuthParameters) = validator("requestToken", "POST") - //Test if the request is valid - if(httpCode==200) - { - //Generate the token and secret - val (token,secret) = generateTokenAndSecret(oAuthParameters.get - ("oauth_consumer_key").get) - //Save the token that we have generated - if(saveRequestToken(oAuthParameters,token, secret)) - data=("oauth_token="+token+"&oauth_token_secret="+ - secret+"&oauth_callback_confirmed=true").getBytes() - } - val headers = ("Content-type" -> "application/x-www-form-urlencoded") :: Nil - //return an HTTP response - Full(InMemoryResponse(data,headers,Nil,httpCode)) - } - case Req("oauth" :: "token" :: Nil,_, PostRequest) => - { - //Extract the OAuth parameters from the header and test if the request is valid - var (httpCode, data, oAuthParameters) = validator("authorizationToken", "POST") - //Test if the request is valid - if(httpCode==200) - { - //Generate the token and secret - val (token,secret) = generateTokenAndSecret(oAuthParameters.get - ("oauth_consumer_key").get) - //Save the token that we have generated - if(saveAuthorizationToken(oAuthParameters,token, secret)) - //remove the request token so the application could not exchange it - //again to get an other access token - Token.find(By(Token.key,oAuthParameters.get("oauth_token").get)) match { - case Full(requestToken) => requestToken.delete_! - case _ => None - } - - data=("oauth_token="+token+"&oauth_token_secret="+secret).getBytes() - } - val headers = ("Content-type" -> "application/x-www-form-urlencoded") :: Nil - //return an HTTP response - Full(InMemoryResponse(data,headers,Nil,httpCode)) - } - } - - //Check if the request (access toke or request token) is valid and return a tuple - def validator(requestType : String, httpMethod : String) = - { - //return a Map containing the OAuth parameters : oauth_prameter -> value - def getAllParameters = - { - //Convert the string containing the list of OAuth parameters to a Map - def toMap(parametersList : String) = - { - //transform the string "oauth_prameter="value"" - //to a tuple (oauth_parameter,Decoded(value)) - def dynamicListExtract(input: String) = - { - val oauthPossibleParameters = List("oauth_consumer_key","oauth_nonce", - "oauth_signature_method", "oauth_timestamp","oauth_version", - "oauth_signature","oauth_callback", "oauth_token","oauth_verifier") - - if (input contains "=") { - val split = input.split("=",2) - val parameterValue = URLDecoder.decode(split(1)).replace("\"","") - //add only OAuth parameters and not empty - if(oauthPossibleParameters.contains(split(0)) && ! parameterValue.isEmpty) - Some(split(0),parameterValue) // return key , value - else - None - } - else - None - } - - Map(parametersList.split(",").flatMap(dynamicListExtract _): _*) - } - - S.request match - { - case Full(a) => a.header("Authorization") match - { - case Full(parameters) => toMap(parameters) - case _ => Map(("","")) - } - case _ => Map(("","")) - } - } - //return true if the authorization header has a duplicated parameter - def duplicatedParameters = - { - var output=false - val authorizationParameters = S.request.get.header("Authorization").get.split(",") - - //count the iterations of a parameter in the authorization header - def countPram(parameterName : String, parametersArray :Array[String] )={ - var i = 0 - parametersArray.foreach(t => {if (t.split("=")(0) == parameterName) i+=1}) - i - } - - //return true if on of the Authorization header parameter is present more than one time - authorizationParameters.foreach(t => { - if(countPram(t.split("=")(0),authorizationParameters)>1 && !output) - output=true - }) - - output - } - - def suportedOAuthVersion(OAuthVersion : Option[String]) : Boolean = { - //auth_version is OPTIONAL. If present, MUST be set to "1.0". - OAuthVersion match - { - case Some(a) => a=="1" || a=="1.0" - case _ => true - } - } - def wrongTimestamp(requestTimestamp : Date) = { - val currentTime = Platform.currentTime - val timeRange : Long = 180000 //3 minutes - //check if the timestamp is positive and in the time range - requestTimestamp.getTime < 0 || requestTimestamp.before(new Date(currentTime - timeRange)) || requestTimestamp.after(new Date(currentTime + timeRange)) - } - - def alReadyUsedNonce(parameters : Map[String, String]) = { - - /*The nonce value MUST be unique across all requests with the - same timestamp, client credentials, and token combinations. - */ - val token = parameters.get("oauth_token") getOrElse "" - - Nonce.findAll(By(Nonce.value,parameters.get("oauth_nonce").get), By(Nonce.tokenKey, token), - By(Nonce.consumerkey,parameters.get("oauth_consumer_key").get), - By(Nonce.timestamp, new Date(parameters.get("oauth_timestamp").get.toLong))).length !=0 - } - def registeredApplication(consumerKey : String ) : Boolean = - { - Consumer.find(By(Consumer.key,consumerKey)) match - { - case Full(application) => application.isActive - case _ => false - } - } - def correctSignature(OAuthparameters : Map[String, String], httpMethod : String) = - { - //Normalize an encode the request parameters as explained in Section 3.4.1.3.2 - //of OAuth 1.0 specification (http://tools.ietf.org/html/rfc5849) - def generateOAuthParametersString(OAuthparameters : Map[String, String]) : String = - { - def sortParam( keyAndValue1 : (String, String), keyAndValue2 : (String, String))= keyAndValue1._1.compareTo(keyAndValue2._1) < 0 - - var parameters ="" - - //sort the parameters by name - OAuthparameters.toList.sort(sortParam _).foreach(t => - if(t._1 != "oauth_signature") - parameters += URLEncoder.encode(t._1,"UTF-8")+"%3D"+ - URLEncoder.encode(t._2,"UTF-8")+"%26" - ) - parameters = parameters.dropRight(3) //remove the "&" encoded sign - parameters - } - - //prepare the base string - var baseString = httpMethod+"&"+URLEncoder.encode(S.hostAndPath,"UTF-8")+"&" - baseString+= generateOAuthParametersString(OAuthparameters) - - //get the key to sign - val comsumer = Consumer.find(By(Consumer.key,OAuthparameters. - get("oauth_consumer_key").get)).get - var secret= comsumer.secret.toString() - OAuthparameters.get("oauth_token") match - { - case Some(tokenKey) => Token.find(By(Token.key,tokenKey)) match { - case Full(token) => secret+= "&" +token.secret.toString() - case _ => None - } - case _ => None - } - - //signing process - var m = Mac.getInstance("HmacSHA256"); - m.init(new SecretKeySpec(secret.getBytes(),"HmacSHA256")) - val calculatedSignature = Helpers.base64Encode(m.doFinal(baseString.getBytes)).dropRight(1) //remove the "=" added by the base64Encode method - - - calculatedSignature==OAuthparameters.get("oauth_signature").get - } - - //check if the token exists and is still valid - def validToken(tokenKey : String, verifier : String) = - { - Token.find(By(Token.key, tokenKey)) match - { - case Full(token) => if(token.expirationDate.compareTo(new Date( - Platform.currentTime)) == 1 && token.verifier==verifier) - true - else - false - case _ => false - } - } - def validToken2(tokenKey : String) = - { - Token.find(By(Token.key, tokenKey)) match - { - case Full(token) => if(token.expirationDate.compareTo(new Date( - Platform.currentTime)) == 1) - true - else - false - case _ => false - } - } - //check if the all the necessary OAuth parameters are present regarding - //the request type - def enoughtOauthParameters(parameters : Map[String, String], requestType : String) : Boolean = - { - val parametersBase = List("oauth_consumer_key","oauth_nonce","oauth_signature_method", - "oauth_timestamp", "oauth_signature") - - if (parameters.size < 6) - false - else if(requestType == "requestToken") - ("oauth_callback" :: parametersBase).toSet.subsetOf(parameters.keySet) - else if(requestType=="authorizationToken") - ("oauth_token" :: "oauth_verifier" :: parametersBase).toSet.subsetOf(parameters.keySet) - else if(requestType=="protectedResource") - ("oauth_token" :: parametersBase).toSet.subsetOf(parameters.keySet) - else - false - } - - var data ="" - var httpCode : Int = 500 - - var parameters = getAllParameters - - //does all the OAuth parameters are presents? - if(! enoughtOauthParameters(parameters,requestType)) - { - data = "One or several parameters are missing" - httpCode = 400 - } - //no parameter exists more than one times - else if (duplicatedParameters) - { - data = "Duplicated protocol parameters" - httpCode = 400 - } - //valid OAuth - else if(!suportedOAuthVersion(parameters.get("oauth_version"))) - { - data = "OAuth version not supported" - httpCode = 400 - } - //supported signature method - else if (parameters.get("oauth_signature_method").get.toLowerCase()!="hmac-sha256") - { - data = "Unsupported signature method" - httpCode = 400 - } - //check if the application is registered and active - else if(! registeredApplication(parameters.get("oauth_consumer_key").get)) - { - data = "Invalid client credentials" - httpCode = 401 - } - //valid timestamp - else if(wrongTimestamp(new Date(parameters.get("oauth_timestamp").get.toLong))) - { - data = "wrong timestamps" - httpCode = 400 - } - //unused nonce - else if (alReadyUsedNonce(parameters)) - { - data = "Nonce already used" - httpCode = 401 - } - //In the case OAuth authorization token request, check if the token is still valid and the verifier is correct - else if(requestType=="authorizationToken" && !validToken(parameters.get("oauth_token").get, parameters.get("oauth_verifier").get)) - { - data = "Invalid or expired token" - httpCode = 401 - } - //In the case protected resource access request, check if the token is still valid - else if (requestType=="protectedResource" && - ! validToken2(parameters.get("oauth_token").get)) - { - data = "Invalid or expired token" - httpCode = 401 - } - //checking if the signature is correct - else if(! correctSignature(parameters, httpMethod)) - { - data = "Invalid signature" - httpCode = 401 - } - else - httpCode = 200 - - (httpCode, data.getBytes(), parameters) - } - private def generateTokenAndSecret(ConsumerKey : String) = - { - import java.util.UUID._ - - // generate random string - val token_data = ConsumerKey + randomUUID().toString() + Helpers.randomString(20) - //use HmacSHA256 to compute the token - val m = Mac.getInstance("HmacSHA256"); - m.init(new SecretKeySpec(token_data.getBytes(),"HmacSHA256")) - val token = Helpers.base64Encode(m.doFinal(token_data.getBytes)).dropRight(1) - - // generate random string - val secret_data = ConsumerKey + randomUUID().toString() + Helpers.randomString(20) + token - //use HmacSHA256 to compute the token - val n = Mac.getInstance("HmacSHA256"); - n.init(new SecretKeySpec(token_data.getBytes(),"HmacSHA256")) - val secret = Helpers.base64Encode(n.doFinal(token_data.getBytes)).dropRight(1) - - (token,secret) - } - private def saveRequestToken(oAuthParameters : Map[String, String], tokenKey : String, tokenSecret : String) = - { - import code.model.{Nonce, Token, TokenType} - - val nonce = Nonce.create - nonce.consumerkey(oAuthParameters.get("oauth_consumer_key").get) - nonce.timestamp(new Date(oAuthParameters.get("oauth_timestamp").get.toLong)) - nonce.value(oAuthParameters.get("oauth_nonce").get) - val nonceSaved = nonce.save() - - val token = Token.create - token.tokenType(TokenType.Request) - Consumer.find(By(Consumer.key,oAuthParameters.get("oauth_consumer_key").get)) match - { - case Full(consumer) => token.consumerId(consumer.id) - case _ => None - } - token.key(tokenKey) - token.secret(tokenSecret) - if(! oAuthParameters.get("oauth_callback").get.isEmpty) - token.callbackURL(URLDecoder.decode(oAuthParameters.get("oauth_callback").get,"UTF-8")) - else - token.callbackURL("oob") - val currentTime = Platform.currentTime - val tokenDuration : Long = 1800000 //the duration is 30 minutes TODO: 300000 in production mode - token.duration(tokenDuration) - token.expirationDate(new Date(currentTime+tokenDuration)) - token.insertDate(new Date(currentTime)) - val tokenSaved = token.save() - - nonceSaved && tokenSaved - } - private def saveAuthorizationToken(oAuthParameters : Map[String, String], tokenKey : String, tokenSecret : String) = - { - import code.model.{Nonce, Token, TokenType} - - val nonce = Nonce.create - nonce.consumerkey(oAuthParameters.get("oauth_consumer_key").get) - nonce.timestamp(new Date(oAuthParameters.get("oauth_timestamp").get.toLong)) - nonce.tokenKey(oAuthParameters.get("oauth_token").get) - nonce.value(oAuthParameters.get("oauth_nonce").get) - val nonceSaved = nonce.save() - - val token = Token.create - token.tokenType(TokenType.Access) - Consumer.find(By(Consumer.key,oAuthParameters.get("oauth_consumer_key").get)) match - { - case Full(consumer) => token.consumerId(consumer.id) - case _ => None - } - Token.find(By(Token.key, oAuthParameters.get("oauth_token").get)) match { - case Full(requestToken) => token.userId(requestToken.userId) - case _ => None - } - token.key(tokenKey) - token.secret(tokenSecret) - val currentTime = Platform.currentTime - val tokenDuration : Long = 86400000 //the duration is 1 day - token.duration(tokenDuration) - token.expirationDate(new Date(currentTime+tokenDuration)) - token.insertDate(new Date(currentTime)) - val tokenSaved = token.save() - - nonceSaved && tokenSaved - } - - // this method is specific to the authorization page ( where the user login to grant access - // to the application (step 2)) - def tokenCheck = - S.param("oauth_token") match - { - case Full(token) => - Token.find(By(Token.key,token.toString)) match - { - case Full(appToken) => - //check if the token is still valid - if(appToken.expirationDate.compareTo(new Date(Platform.currentTime)) == 1) - if(OBPUser.loggedIn_?) - { - var verifier ="" - // if the user is logged in and non verifier have been generated - if(appToken.verifier.isEmpty) - { - val randomVerifier = Helpers.base64Encode(Helpers.randomString(20).getBytes()).dropRight(1) - appToken.verifier(randomVerifier) - appToken.userId(OBPUser.currentUserId.get.toLong) - if(appToken.save()) - verifier = randomVerifier - } - else - verifier=appToken.verifier - - // show the verifier if the application does not support - // redirection - if(Token.callbackURL=="oob") - "#verifier " #> verifier - else - { - //redirect the user to the application with the verifier - S.redirectTo(appToken.callbackURL+"?oauth_token="+token+ - "&oauth_verifier="+verifier) - "#verifier" #> "you should be redirected" - } - } - else - //the user is not logged in so we show a login form - Consumer.find(By(Consumer.id,appToken.consumerId)) match - { - case Full(consumer) => - { - "#applicationName" #> consumer.name & - "#verifier" #>NodeSeq.Empty & - "#errorMessage" #> NodeSeq.Empty & - { - ".login [action]" #> OBPUser.loginPageURL & - ".forgot [href]" #> - { - val href = for { - menu <- OBPUser.resetPasswordMenuLoc - } yield menu.loc.calcDefaultHref - href getOrElse "#" - } & - ".signup [href]" #> - OBPUser.signUpPath.foldLeft("")(_ + "/" + _) - } - } - case _ => - { - "#errorMessage" #> "Application not found" & - "#userAccess" #> NodeSeq.Empty - } - } - else - { - "#errorMessage" #> "Token expired" & - "#userAccess" #> NodeSeq.Empty - } - case _ => - { - "#errorMessage" #> "This token does not exist" & - "#userAccess" #> NodeSeq.Empty - } - } - case _ => - { - "#errorMessage" #> "There is no Token"& - "#userAccess" #> NodeSeq.Empty - } - } - - //looks for expired tokens and nonces and delete them - def dataBaseCleaner : Unit = - { - import net.liftweb.util.Schedule - import net.liftweb.mapper.By_< - Schedule.schedule(dataBaseCleaner _, 1 hour) - - val currentDate = new Date() - - /* - As in "wrong timestamp" function, 3 minutes is the timestamp limit where we accept - requests. So this function will delete nonces which have a timestamp older than - currentDate - 3 minutes - */ - val timeLimit = new Date(currentDate.getTime + 180000) - - //delete expired tokens and nonces - (Token.findAll(By_<(Token.expirationDate,currentDate)) ++ Nonce.findAll(By_<(Nonce.timestamp,timeLimit))).foreach(t => t.delete_!) - } -} \ No newline at end of file diff --git a/MavLift/src/main/scala/code/snippet/OBPREST.scala b/MavLift/src/main/scala/code/snippet/OBPREST.scala deleted file mode 100644 index 0ce33a797..000000000 --- a/MavLift/src/main/scala/code/snippet/OBPREST.scala +++ /dev/null @@ -1,488 +0,0 @@ -/** -Open Bank Project - Transparency / Social Finance Web Application -Copyright (C) 2011, 2012, TESOBE / Music Pictures 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 . - -Email: contact@tesobe.com -TESOBE / Music Pictures 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 com.tesobe.utils { - -import code.actors.EnvelopeInserter -import net.liftweb.http._ -import net.liftweb.http.rest._ -import net.liftweb.json.JsonDSL._ -import net.liftweb.json.Printer._ -import net.liftweb.json.Extraction._ -import net.liftweb.json.JsonAST._ -import java.util.Calendar -import net.liftweb.common.Failure -import net.liftweb.common.Full -import net.liftweb.common.Empty -import net.liftweb.mongodb._ -import net.liftweb.json.JsonAST.JString -import com.mongodb.casbah.Imports._ -import _root_.java.math.MathContext -import org.bson.types._ -import org.joda.time.{DateTime, DateTimeZone} -import java.util.regex.Pattern -import _root_.net.liftweb.common._ -import _root_.net.liftweb.util._ -import _root_.net.liftweb.http._ -import _root_.net.liftweb.mapper._ -import _root_.net.liftweb.util.Helpers._ -import _root_.net.liftweb.sitemap._ -import _root_.scala.xml._ -import _root_.net.liftweb.http.S._ -import _root_.net.liftweb.http.RequestVar -import _root_.net.liftweb.util.Helpers._ -import _root_.net.liftweb.common.Full -import net.liftweb.mongodb.{Skip, Limit} -import _root_.net.liftweb.http.S._ -import _root_.net.liftweb.mapper.view._ -import com.mongodb._ -import code.model.dataAccess.{ Account, OBPEnvelope, OBPUser } -import code.model.dataAccess.HostedAccount -import code.model.dataAccess.LocalStorage -import code.model.traits.ModeratedTransaction -import code.model.traits.View -import code.model.implementedTraits.View -import code.model.dataAccess.OBPEnvelope._ -import code.model.traits.BankAccount -import code.model.implementedTraits.Anonymous -import code.model.traits.Bank -import code.model.traits.User -import java.util.Date -import code.snippet.OAuthHandshake._ -import code.model.traits.ModeratedBankAccount - - object OBPRest extends RestHelper with Loggable { - - val dateFormat = ModeratedTransaction.dateFormat - private def getUser(httpCode : Int, tokenID : Box[String]) : Box[OBPUser] = - if(httpCode==200) - { - import code.model.Token - Token.find(By(Token.key, tokenID.get)) match { - case Full(token) => OBPUser.find(By(OBPUser.id, token.userId)) - case _ => Empty - } - } - else - Empty - - serve("obp" / "v1.0" prefix { - - case Nil JsonGet json => { - - 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 bankAlias :: "accounts" :: accountAlias :: "transactions" :: viewName :: Nil JsonGet json => { - import code.snippet.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 bankAccount = BankAccount(bankAlias, accountAlias) - 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(_)) - - def getTransactions(bankAccount: BankAccount, view: View, user: Option[OBPUser]) = { - if(bankAccount.authorisedAccess(view, user)) { - val basicParams = List(OBPLimit(limit), - OBPOffset(offset), - OBPOrdering(sortBy, sortDirection)) - - val params : List[OBPQueryParam] = fromDate.toList ::: toDate.toList ::: basicParams - bankAccount.getModeratedTransactions(params: _*)(view.moderate) - } else Nil - } - - val response = for { - bankAccount <- BankAccount(bankAlias, accountAlias) - view <- View.fromUrl(viewName) //TODO: This will have to change if we implement custom view names for different accounts - } yield { - val ts = getTransactions(bankAccount, view, getUser(httpCode,oAuthParameters.get("oauth_token"))) - JsonResponse("transactions" -> ts.map(t => t.toJson(view))) - } - - response getOrElse InMemoryResponse(data, headers, Nil, 401) : LiftResponse - } - - case bankAlias :: "accounts" :: accountAlias :: "transactions" :: - transactionID :: "transaction" :: viewName :: Nil JsonGet json => { - - val (httpCode, data, oAuthParameters) = validator("protectedResource", "GET") - val user = getUser(httpCode,oAuthParameters.get("oauth_token")) - - val moderatedTransactionAndView = for { - bank <- Bank(bankAlias) ?~ { "bank " + bankAlias + " not found"} ~> 404 - account <- BankAccount(bankAlias, accountAlias) ?~ { "account " + accountAlias + " not found for bank"} ~> 404 - view <- View.fromUrl(viewName) ?~ { "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 bankAlias :: "accounts" :: accountAlias :: "transactions" :: - transactionID :: "comments" :: viewName :: Nil JsonGet json => { - - val (httpCode, data, oAuthParameters) = validator("protectedResource", "GET") - val user = getUser(httpCode,oAuthParameters.get("oauth_token")) - - val comments = for { - bank <- Bank(bankAlias) ?~ { "bank " + bankAlias + " not found"} ~> 404 - account <- BankAccount(bankAlias, accountAlias) ?~ { "account " + accountAlias + " not found for bank"} ~> 404 - view <- View.fromUrl(viewName) ?~ { "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 bankPermalink :: "accounts" :: Nil JsonGet json => { - 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(bankPermalink) match { - case Full(bank) => - { - val availableAccounts = bank.accounts.filter(_.permittedViews(user).size!=0) - if(availableAccounts.size!=0) - bankAccountSet2JsonResponse(availableAccounts) - else - InMemoryResponse(data, headers, Nil, httpCode) - } - case _ => { - val error = "bank " + bankPermalink + " not found" - InMemoryResponse(error.getBytes(), headers, Nil, 404) - } - } - } - - case bankAlias :: "accounts" :: accountAlias :: "account" :: viewName :: Nil JsonGet json => { - 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: Set[View]) - - val moderatedAccountAndViews = for { - bank <- Bank(bankAlias) ?~ { "bank " + bankAlias + " not found"} ~> 404 - account <- BankAccount(bankAlias, accountAlias) ?~ { "account " + accountAlias + " not found for bank"} ~> 404 - view <- View.fromUrl(viewName) ?~ { "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" -> { "/" + bankAlias + "/accounts/" + accountAlias + "/transactions/" + view.permalink }) ~ - ("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 bankAlias :: "offices" :: Nil JsonGet json => { - //TODO: An office model needs to be created - val offices : List[JObject] = Nil - JsonResponse("offices" -> offices) - } - - case bankAlias :: "bank" :: Nil JsonGet json => { - - def links = { - def accounts = { - ("rel" -> "accounts") ~ - ("href" -> {"/" + bankAlias + "/accounts"}) ~ - ("method" -> "GET") ~ - ("title" -> "Get list of accounts available") - } - - def offices = { - ("rel" -> "offices") ~ - ("href" -> {"/" + bankAlias + "/offices"}) ~ - ("method" -> "GET") ~ - ("title" -> "Get list of offices") - } - - List(accounts, offices) - } - - val bank = for { - bank <- Bank(bankAlias) ?~ { "bank " + bankAlias + " not found"} ~> 404 - } yield bank - - bank.map(b => JsonResponse(b.detailedJson ~ ("links" -> links))) - } - - case "banks" :: Nil JsonGet json => { - JsonResponse("banks" -> Bank.toJson(Bank.all)) - } - } - ) - - serve { - //a temporary way to add transaction via api for a single specific exception case. should be removed later. - case "api" :: "tmp" :: "transactions" :: Nil JsonPost json => { - val secretKey = S.param("secret") - - def addMatchingTransactions(secret: String) = { - val rawEnvelopes = json._1.children - val envelopes = rawEnvelopes.flatMap(OBPEnvelope.fromJValue) - val matchingEnvelopes = for { - e <- envelopes - bankName <- Props.get("exceptional_account_bankName") - number <- Props.get("exceptional_account_number") - kind <- Props.get("exceptional_account_kind") - if(e.obp_transaction.get.this_account.get.bank.get.name.get == bankName) - if(e.obp_transaction.get.this_account.get.number.get == number) - if(e.obp_transaction.get.this_account.get.kind.get == kind) - } yield e - - val ipAddress = json._2.remoteAddr - logger.info("Received " + rawEnvelopes.size + - " json transactions to insert from ip address " + ipAddress) - logger.info("Received " + matchingEnvelopes.size + - " valid transactions to insert from ip address " + ipAddress) - - /** - * Using an actor to do insertions avoids concurrency issues with - * duplicate transactions by processing transaction batches one - * at a time. We'll have to monitor this to see if non-concurrent I/O - * is too inefficient. If it is, we could break it up into one actor - * per "Account". - */ - val createdEnvelopes = EnvelopeInserter !? (3 seconds, matchingEnvelopes) - - createdEnvelopes match { - case Full(l: List[JObject]) =>{ - if(matchingEnvelopes.size!=0) - { - Account.find(("number" -> Props.get("exceptional_account_number").getOrElse("")) ~ - ("bankName" -> Props.get("exceptional_account_bankName").getOrElse("")) ~ - ("kind" -> Props.get("exceptional_account_kind").getOrElse(""))) - match { - case Full(account) => account.lastUpdate(new Date).save - case _ => - } - } - JsonResponse(JArray(l)) - } - case _ => InternalServerErrorResponse() - } - } - - def valid(secret : String) = { - val authorised = for (validSecret <- Props.get("exceptional_account_secret")) - yield secret == validSecret - - authorised getOrElse false - } - - secretKey match { - case Full(s) => if(valid(s)) - addMatchingTransactions(s) - else - UnauthorizedResponse("wrong secret key") - case _ => NotFoundResponse() - } - - } - } - - serve { - - /** - * curl -i -H "Content-Type: application/json" -X POST -d '{ - * "obp_transaction":{ - * "this_account":{ - * "holder":"Music Pictures Limited", - * "number":"123567", - * "kind":"current", - * "bank":{ - * "IBAN":"DE1235123612", - * "national_identifier":"de.10010010", - * "name":"Postbank" - * } - * }, - * "other_account":{ - * "holder":"Client 1", - * "number":"123567", - * "kind":"current", - * "bank":{ - * "IBAN":"UK12222879", - * "national_identifier":"uk.10010010", - * "name":"HSBC" - * } - * }, - * "details":{ - * "type_en":"Transfer", - * "type_de":"Überweisung", - * "posted":{ - * "$dt":"2012-01-04T18:06:22.000Z" - * }, - * "completed":{ - * "$dt":"2012-09-04T18:52:13.000Z" - * }, - * "new_balance":{ - * "currency":"EUR", - * "amount":"4323.45" - * }, - * "value":{ - * "currency":"EUR", - * "amount":"123.45" - * }, - * "other_data":"9" - * } - * } - * } ' http://localhost:8080/api/transactions - */ - case "api" :: "transactions" :: Nil JsonPost json => { - - // - // WARNING! - // - // If you have not configured a web server to restrict this URL - // appropriately, anyone will be - // able to post transactions to your database. This would obviously - // be undesirable. So you should - // definitely sort that out. - // - // - - val rawEnvelopes = json._1.children - - val envelopes = rawEnvelopes.map(e => { - OBPEnvelope.fromJValue(e) - }) - - val ipAddress = json._2.remoteAddr - logger.info("Received " + rawEnvelopes.size + - " json transactions to insert from ip address " + ipAddress) - logger.info("Received " + envelopes.size + - " valid transactions to insert from ip address " + ipAddress) - - /** - * Using an actor to do insertions avoids concurrency issues with - * duplicate transactions by processing transaction batches one - * at a time. We'll have to monitor this to see if non-concurrent I/O - * is too inefficient. If it is, we could break it up into one actor - * per "Account". - */ - val createdEnvelopes = EnvelopeInserter !? (3 seconds, envelopes.flatten) - - createdEnvelopes match { - case Full(l: List[JObject]) =>{ - if(envelopes.size!=0) - { - //we assume here that all the Envelopes concerns only one account - val accountNumber = envelopes(0).get.obp_transaction.get.this_account.get.number.get - val bankName = envelopes(0).get.obp_transaction.get.this_account.get.bank.get.name.get - val accountKind = envelopes(0).get.obp_transaction.get.this_account.get.kind.get - Account.find(("number" -> accountNumber) ~ ("bankName" -> bankName) ~ ("kind" -> accountKind)) - match { - case Full(account) => account.lastUpdate(new Date).save - case _ => - } - } - JsonResponse(JArray(l)) - } - case _ => InternalServerErrorResponse() - } - } - } - } -} diff --git a/MavLift/src/main/scala/code/util/APIUtil.scala b/MavLift/src/main/scala/code/util/APIUtil.scala new file mode 100644 index 000000000..ee792750c --- /dev/null +++ b/MavLift/src/main/scala/code/util/APIUtil.scala @@ -0,0 +1,62 @@ +package code.util + +import code.model.dataAccess.APIMetric +import code.api.v1_2.ErrorMessage +import net.liftweb.http.JsonResponse +import net.liftweb.json.Extraction +import net.liftweb.json.JsonAST.JValue +import net.liftweb.http.js.JsExp +import net.liftweb.common.Full +import net.liftweb.util.Helpers._ +import net.liftweb.http.S +import net.liftweb.http.js.JE.JsRaw + +object APIUtil { + + implicit val formats = net.liftweb.json.DefaultFormats + implicit def errorToJson(error: ErrorMessage): JValue = Extraction.decompose(error) + + def httpMethod : String = + S.request match { + case Full(r) => r.request.method + case _ => "GET" + } + + def isThereAnOAuthHeader : Boolean = { + S.request match { + case Full(a) => a.header("Authorization") match { + case Full(parameters) => parameters.contains("OAuth") + case _ => false + } + case _ => false + } + } + + def logAPICall = + APIMetric.createRecord. + url(S.uriAndQueryString.getOrElse("")). + date((now: TimeSpan)). + save + + 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 "" + } + + def noContentJsonResponse : JsonResponse = + JsonResponse(JsRaw(""), Nil, Nil, 204) + + def successJsonResponse(json: JsExp, httpCode : Int = 200) : JsonResponse = + JsonResponse(json, Nil, Nil, httpCode) + + def errorJsonResponse(message : String = "error", httpCode : Int = 400) : JsonResponse = + JsonResponse(Extraction.decompose(ErrorMessage(message)), Nil, Nil, httpCode) + + def oauthHeaderRequiredJsonResponce : JsonResponse = + JsonResponse(Extraction.decompose(ErrorMessage("Authentication via OAuth is required")), Nil, Nil, 400) + +} diff --git a/MavLift/src/main/webapp/connect.html b/MavLift/src/main/webapp/connect.html new file mode 100644 index 000000000..a027d362c --- /dev/null +++ b/MavLift/src/main/webapp/connect.html @@ -0,0 +1,166 @@ + + +
+

+ You can connect to your bank account using this page. +

+
+

+ Once an account is connected you can view your transaction data here - and can share the data with other people too. +

+
+
message
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Please complete the information below so we can connect to your bank account. + *
+ + + + + + error +
+ + + + + + error +
+ + + + + + error +
+ + more info + + + + + error +
+ + more info + + + + + error +
+ + more info + + + + + error +
+ + more info + + + + + error +
+ Public view + more info + + + error +
+ + (*) : Your PIN code will be securely encrypted. We will not disclose your login data to anyone. + +
+ + +
+
+
+
+
\ No newline at end of file diff --git a/MavLift/src/main/webapp/consumer-registration.html b/MavLift/src/main/webapp/consumer-registration.html new file mode 100644 index 000000000..0a4974a02 --- /dev/null +++ b/MavLift/src/main/webapp/consumer-registration.html @@ -0,0 +1,105 @@ + + +
+
+

Register to use the Open Bank API with your application:

+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Please complete the information below so we can get an OAuth key and secret. +
+ + + +
+ + + +
+ + + +
+ + + +
+ + +
+
+
+

Thank you for registering to use the Open Bank API. Here is your developer information. Please save it in a secure location.

+
+
Application Name: ABC
+
Application Type: web
+
Description: ABCDEF
+
Developer Email: abc@example.com
+
+
+
Consumer Key: 23432432432432
+
Consumer Secret: 3334543543543
+
+
+
+
\ No newline at end of file diff --git a/MavLift/src/main/webapp/index.html b/MavLift/src/main/webapp/index.html index 076567faa..f8f5e836c 100755 --- a/MavLift/src/main/webapp/index.html +++ b/MavLift/src/main/webapp/index.html @@ -1,4 +1,4 @@ -
- Hello ! +

+ Welcome to The Open Bank Project API. +

diff --git a/MavLift/src/main/scala/code/model/implementedTraits/AccountOwnerImpl.scala b/MavLift/src/main/webapp/metrics.html similarity index 73% rename from MavLift/src/main/scala/code/model/implementedTraits/AccountOwnerImpl.scala rename to MavLift/src/main/webapp/metrics.html index cd1d1bdf1..b6159299d 100644 --- a/MavLift/src/main/scala/code/model/implementedTraits/AccountOwnerImpl.scala +++ b/MavLift/src/main/webapp/metrics.html @@ -1,4 +1,4 @@ -/** + +
- */ -package code.model.implementedTraits - -import code.model.traits.{BankAccount,AccountOwner} - -class AccountOwnerImpl(id_ : String, accountName : String, ownedBankAccounts: Set[BankAccount]) extends AccountOwner{ - def id = id_ - def name = accountName - def bankAccounts = ownedBankAccounts -} + +

Openbank Graphs

+
+ + +
diff --git a/MavLift/src/main/webapp/templates-hidden/default.html b/MavLift/src/main/webapp/templates-hidden/default.html index 6bc84319a..5bdb989a7 100755 --- a/MavLift/src/main/webapp/templates-hidden/default.html +++ b/MavLift/src/main/webapp/templates-hidden/default.html @@ -1,4 +1,4 @@ - -
+
- The main content gets bound here + The main content gets bound here
- + diff --git a/MavLift/src/test/scala/LiftConsole.scala b/MavLift/src/test/scala/LiftConsole.scala index 6a8c608c7..a4ea1c9ba 100755 --- a/MavLift/src/test/scala/LiftConsole.scala +++ b/MavLift/src/test/scala/LiftConsole.scala @@ -17,6 +17,7 @@ limitations under the License. */ import bootstrap.liftweb.Boot import scala.tools.nsc.MainGenericRunner +import scala.sys._ object LiftConsole { def main(args : Array[String]) { @@ -27,6 +28,6 @@ object LiftConsole { // Now run the MainGenericRunner to get your repl MainGenericRunner.main(args) // After the repl exits, then exit the scala script - exit(0) + sys.exit(0) } } diff --git a/MavLift/src/test/scala/RunWebApp.scala b/MavLift/src/test/scala/RunWebApp.scala index e7c7b8130..dce00d5a2 100755 --- a/MavLift/src/test/scala/RunWebApp.scala +++ b/MavLift/src/test/scala/RunWebApp.scala @@ -32,8 +32,9 @@ import org.mortbay.jetty.Connector import org.mortbay.jetty.Server import org.mortbay.jetty.webapp.WebAppContext import org.mortbay.jetty.nio._ +import scala.sys._ -object RunWebApp extends Application { +object RunWebApp extends App { val server = new Server val scc = new SelectChannelConnector scc.setPort(8080) @@ -57,7 +58,7 @@ object RunWebApp extends Application { } catch { case exc : Exception => { exc.printStackTrace() - System.exit(100) + sys.exit(100) } } } diff --git a/MavLift/src/test/scala/code/api/API11Test.scala b/MavLift/src/test/scala/code/api/API11Test.scala new file mode 100644 index 000000000..1ba746aaa --- /dev/null +++ b/MavLift/src/test/scala/code/api/API11Test.scala @@ -0,0 +1,30 @@ +package code.api.v1_1 + +import org.scalatest._ +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner +import dispatch._ +import code.api.test.{ServerSetup, APIResponse} + + +@RunWith(classOf[JUnitRunner]) +class API1_1Test extends ServerSetup{ + + val v1_1Request = baseRequest / "obp" / "v1.1" + + def getAPIInfo = { + val request = v1_1Request + makeGetRequest(request) + } + + /************************ the tests ************************/ + feature("base line URL works"){ + scenario("we get the api information") { + Given("The user is not logged in") + When("the request is sent") + val reply = getAPIInfo + Then("we should get a 200 created code") + reply.code should equal (200) + } + } +} diff --git a/MavLift/src/test/scala/code/api/API12Test.scala b/MavLift/src/test/scala/code/api/API12Test.scala new file mode 100644 index 000000000..d9ace9d8a --- /dev/null +++ b/MavLift/src/test/scala/code/api/API12Test.scala @@ -0,0 +1,5493 @@ +package code.api.v1_2 + +import org.scalatest._ +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner +import _root_.net.liftweb.util._ +import Helpers._ +import _root_.net.liftweb.common._ +import dispatch._ +import oauth._ +import OAuth._ +import _root_.net.liftweb.json.Extraction +import _root_.net.liftweb.json.Serialization +import _root_.net.liftweb.json.Serialization.write +import dispatch.liftjson.Js._ +import _root_.net.liftweb.json.JsonAST.{JValue, JObject} +import org.mortbay.jetty.nio.SelectChannelConnector +import net.liftweb.json.NoTypeHints +import net.liftweb.json.JsonDSL._ +import scala.util.Random._ +import net.liftweb.mapper.By +import java.util.Date + +import code.model.TokenType._ +import code.model.{Consumer => OBPConsumer, Token => OBPToken} +import code.model.dataAccess.{OBPUser, Privilege, HostedAccount} +import code.api.test.{ServerSetup, APIResponse} +import code.model.dataAccess.{OBPUser, Privilege, HostedAccount, Account} + + +class API1_2Test extends ServerSetup{ + + val v1_2Request = baseRequest / "obp" / "v1.2" + implicit val dateFormats = net.liftweb.json.DefaultFormats + //create the application + lazy val testConsumer = + OBPConsumer.create. + name("test application"). + isActive(true). + key(randomString(40).toLowerCase). + secret(randomString(40).toLowerCase). + saveMe + + lazy val consumer = new Consumer (testConsumer.key,testConsumer.secret) + // create the access token + lazy val tokenDuration = weeks(4) + + lazy val user1 = + OBPUser.create. + email("testuser1@example.com"). + password(randomString(10)). + validated(true). + firstName(randomString(10)). + lastName(randomString(10)). + saveMe + + override def specificSetup() ={ + //give to user1 all the privileges on all the accounts + HostedAccount.findAll.foreach(bankAccount => { + Privilege.create. + account(bankAccount). + ownerPermission(true). + mangementPermission(true). + authoritiesPermission(true). + boardPermission(true). + teamPermission(true). + ourNetworkPermission(true). + user(user1). + save + }) + } + + lazy val testToken = + OBPToken.create. + tokenType(Access). + consumerId(testConsumer.id). + userId(user1.email). + key(randomString(40).toLowerCase). + secret(randomString(40).toLowerCase). + duration(tokenDuration). + expirationDate({(now : TimeSpan) + tokenDuration}). + insertDate(now). + saveMe + + lazy val token = new Token(testToken.key, testToken.secret) + + // create a user for test purposes + lazy val user2 = + OBPUser.create. + email("testuser2@example.com"). + password(randomString(10)). + validated(true). + firstName(randomString(10)). + lastName(randomString(10)). + saveMe + + //we create an access token for the other user + lazy val testToken2 = + OBPToken.create. + tokenType(Access). + consumerId(testConsumer.id). + userId(user2.email). + key(randomString(40).toLowerCase). + secret(randomString(40).toLowerCase). + duration(tokenDuration). + expirationDate({(now : TimeSpan) + tokenDuration}). + insertDate(now). + saveMe + + lazy val token2 = new Token(testToken2.key, testToken2.secret) + + // create a user for test purposes + lazy val user3 = + OBPUser.create. + email("testuser3@example.com"). + password(randomString(10)). + validated(true). + firstName(randomString(10)). + lastName(randomString(10)). + saveMe + + //we create an access token for the other user + lazy val testToken3 = + OBPToken.create. + tokenType(Access). + consumerId(testConsumer.id). + userId(user3.email). + key(randomString(40).toLowerCase). + secret(randomString(40).toLowerCase). + duration(tokenDuration). + expirationDate({(now : TimeSpan) + tokenDuration}). + insertDate(now). + saveMe + + lazy val token3 = new Token(testToken3.key, testToken3.secret) + + //Note: for the moment we have a limited number of views, so the following list contains permalinks of all the views except Full, Base and Public. + val possibleViewsPermalinks = List("team", "board", "authorities", "our-network", "owner") + val possibleViewsPermalinksAllowingViewPrivilige = List("team", "board", "authorities", "owner") + + /************************* test tags ************************/ + + object CurrentTest extends Tag("currentScenario") + object API1_2 extends Tag("api1.2") + object APIInfo extends Tag("apiInfo") + object GetHostedBanks extends Tag("hostedBanks") + object GetHostedBank extends Tag("getHostedBank") + object GetBankAccounts extends Tag("getBankAccounts") + object GetPublicBankAccounts extends Tag("getPublicBankAccounts") + object GetPrivateBankAccounts extends Tag("getPrivateBankAccounts") + object GetBankAccount extends Tag("getBankAccount") + object GetViews extends Tag("getViews") + object GetPermissions extends Tag("getPermissions") + object GetPermission extends Tag("getPermission") + object PostPermission extends Tag("postPermission") + object PostPermissions extends Tag("postPermissions") + object DeletePermission extends Tag("deletePermission") + object DeletePermissions extends Tag("deletePermissions") + object GetOtherBankAccounts extends Tag("getOtherBankAccounts") + object GetOtherBankAccount extends Tag("getOtherBankAccount") + object GetOtherBankAccountMetadata extends Tag("getOtherBankAccountMetadata") + object GetPublicAlias extends Tag("getPublicAlias") + object PostPublicAlias extends Tag("postPublicAlias") + object PutPublicAlias extends Tag("putPublicAlias") + object DeletePublicAlias extends Tag("deletePublicAlias") + object GetPrivateAlias extends Tag("getPrivateAlias") + object PostPrivateAlias extends Tag("postPrivateAlias") + object PutPrivateAlias extends Tag("putPrivateAlias") + object DeletePrivateAlias extends Tag("deletePrivateAlias") + object PostMoreInfo extends Tag("postMoreInfo") + object PutMoreInfo extends Tag("putMoreInfo") + object DeleteMoreInfo extends Tag("deleteMoreInfo") + object PostURL extends Tag("postURL") + object PutURL extends Tag("putURL") + object DeleteURL extends Tag("deleteURL") + object PostImageURL extends Tag("postImageURL") + object PutImageURL extends Tag("putImageURL") + object DeleteImageURL extends Tag("DeleteImageURL") + object PostOpenCorporatesURL extends Tag("postOpenCorporatesURL") + object PutOpenCorporatesURL extends Tag("putOpenCorporatesURL") + object DeleteOpenCorporatesURL extends Tag("deleteOpenCorporatesURL") + object PostCorporateLocation extends Tag("postCorporateLocation") + object PutCorporateLocation extends Tag("putCorporateLocation") + object DeleteCorporateLocation extends Tag("deleteCorporateLocation") + object PostPhysicalLocation extends Tag("postPhysicalLocation") + object PutPhysicalLocation extends Tag("putPhysicalLocation") + object DeletePhysicalLocation extends Tag("deletePhysicalLocation") + object GetTransactions extends Tag("getTransactions") + object GetTransaction extends Tag("getTransaction") + object GetNarrative extends Tag("getNarrative") + object PostNarrative extends Tag("postNarrative") + object PutNarrative extends Tag("putNarrative") + object DeleteNarrative extends Tag("deleteNarrative") + object GetComments extends Tag("getComments") + object PostComment extends Tag("postComment") + object DeleteComment extends Tag("deleteComment") + object GetTags extends Tag("getTags") + object PostTag extends Tag("postTag") + object DeleteTag extends Tag("deleteTag") + object GetImages extends Tag("getImages") + object PostImage extends Tag("postImage") + object DeleteImage extends Tag("deleteImage") + object GetWhere extends Tag("getWhere") + object PostWhere extends Tag("postWhere") + object PutWhere extends Tag("putWhere") + object DeleteWhere extends Tag("deleteWhere") + object GetTransactionAccount extends Tag("getTransactionAccount") + + /********************* API test methods ********************/ + val emptyJSON : JObject = + ("error" -> "empty List") + val errorAPIResponse = new APIResponse(400,emptyJSON) + + def randomViewPermalink : String = { + val randomPosition = nextInt(possibleViewsPermalinks.size) + possibleViewsPermalinks(randomPosition) + } + + def randomViewPermalinkAllowingViewPrivilige : String = { + val randomPosition = nextInt(possibleViewsPermalinksAllowingViewPrivilige.size) + possibleViewsPermalinksAllowingViewPrivilige(randomPosition) + } + + def randomBank : String = { + val banksJson = getBanksInfo.body.extract[BanksJSON] + val randomPosition = nextInt(banksJson.banks.size) + val bank = banksJson.banks(randomPosition) + bank.id + } + + def randomPublicAccount(bankId : String) : AccountJSON = { + val accountsJson = getPublicAccounts(bankId).body.extract[AccountsJSON].accounts + val randomPosition = nextInt(accountsJson.size) + accountsJson(randomPosition) + } + + def randomPrivateAccount(bankId : String) : AccountJSON = { + val accountsJson = getPrivateAccounts(bankId).body.extract[AccountsJSON].accounts + val randomPosition = nextInt(accountsJson.size) + accountsJson(randomPosition) + } + + def randomAccountPermission(bankId : String, accountId : String) : PermissionJSON = { + val persmissionsInfo = getAccountPermissions(bankId, accountId).body.extract[PermissionsJSON] + val randomPermission = nextInt(persmissionsInfo.permissions.size) + persmissionsInfo.permissions(randomPermission) + } + + def randomOtherBankAccount(bankId : String, accountId : String, viewId : String): OtherAccountJSON = { + val otherAccounts = getTheOtherBankAccounts(bankId, accountId, viewId).body.extract[OtherAccountsJSON].other_accounts + otherAccounts(nextInt(otherAccounts.size)) + } + + def randomLocation : LocationPlainJSON = { + def sign = { + val b = nextBoolean + if(b) 1 + else -1 + } + val longitude : Double = nextInt(180)*sign*nextDouble + val latitude : Double = nextInt(90)*sign*nextDouble + JSONFactory.createLocationPlainJSON(latitude, longitude) + } + + def randomTransaction(bankId : String, accountId : String, viewId: String) : TransactionJSON = { + val transactionsJson = getTransactions(bankId, accountId, viewId).body.extract[TransactionsJSON].transactions + val randomPosition = nextInt(transactionsJson.size) + transactionsJson(randomPosition) + } + + def randomViewsIdsToGrant(bankId : String, accountId : String) : List[String]= { + //get the view ids of the available views on the bank accounts + val viewsIds = getAccountViews(bankId, accountId).body.extract[ViewsJSON].views.map(_.id) + //choose randomly some view ids to grant + val (viewsIdsToGrant, _) = viewsIds.splitAt(nextInt(viewsIds.size) + 1) + viewsIdsToGrant + } + + def getAPIInfo : h.HttpPackage[APIResponse] = { + val request = v1_2Request + makeGetRequest(request) + } + + def getBanksInfo : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" + makeGetRequest(request) + } + + def getBankInfo(bankId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId + makeGetRequest(request) + } + + def getPublicAccounts(bankId : String) : h.HttpPackage[APIResponse]= { + val request = v1_2Request / "banks" / bankId / "accounts" / "public" + makeGetRequest(request) + } + + def getPrivateAccounts(bankId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / "private" <@(consumer,token) + makeGetRequest(request) + } + + def getBankAccounts(bankId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" + makeGetRequest(request) + } + + def getBankAccountsWithToken(bankId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" <@(consumer,token) + makeGetRequest(request) + } + + def getPrivateAccountsWithOutToken(bankId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / "private" + makeGetRequest(request) + } + + def getPublicBankAccountDetails(bankId : String, accountId : String, viewId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "account" + makeGetRequest(request) + } + + def getPrivateBankAccountDetails(bankId : String, accountId : String, viewId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "account" <@(consumer,token) + makeGetRequest(request) + } + + def getAccountViews(bankId : String, accountId : String): h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / "views" <@(consumer,token) + makeGetRequest(request) + } + + def getAccountViewsWithoutToken(bankId : String, accountId : String): h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / "views" + makeGetRequest(request) + } + + def getAccountViewsWithoutOwnerAccess(bankId : String, accountId : String): h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / "views" <@(consumer,token3) + makeGetRequest(request) + } + + def getAccountPermissions(bankId : String, accountId : String): h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions" <@(consumer,token) + makeGetRequest(request) + } + + def getAccountPermissionsWithoutToken(bankId : String, accountId : String) : h.HttpPackage[APIResponse]= { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions" + makeGetRequest(request) + } + + def getAccountPermissionsWithoutOwnerAccess(bankId : String, accountId : String) : h.HttpPackage[APIResponse]= { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions" <@(consumer,token2) + makeGetRequest(request) + } + + def getUserAccountPermission(bankId : String, accountId : String, userId : String) : h.HttpPackage[APIResponse]= { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId <@(consumer,token) + makeGetRequest(request) + } + + def getUserAccountPermissionWithoutToken(bankId : String, accountId : String, userId : String) : h.HttpPackage[APIResponse]= { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId + makeGetRequest(request) + } + + def grantUserAccessToView(bankId : String, accountId : String, userId : String, viewId : String) : h.HttpPackage[APIResponse]= { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId / "views" / viewId).POST.<@(consumer,token) + makePostRequest(request) + } + + def grantUserAccessToViewWithWrongUser(bankId : String, accountId : String, userId : String, viewId : String) : h.HttpPackage[APIResponse]= { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId / "views" / viewId).POST.<@(consumer,token3) + makePostRequest(request) + } + + def grantUserAccessToViews(bankId : String, accountId : String, userId : String, viewIds : List[String]) : h.HttpPackage[APIResponse]= { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId / "views").POST.<@(consumer,token) + val viewsJson = ViewIdsJson(viewIds) + makePostRequest(request, write(viewsJson)) + } + + def grantUserAccessToViewsWithWrongUser(bankId : String, accountId : String, userId : String, viewIds : List[String]) : h.HttpPackage[APIResponse]= { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId / "views").POST.<@(consumer,token3) + val viewsJson = ViewIdsJson(viewIds) + makePostRequest(request, write(viewsJson)) + } + + def revokeUserAccessToView(bankId : String, accountId : String, userId : String, viewId : String) : h.HttpPackage[APIResponse]= { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId / "views" / viewId).DELETE.<@(consumer,token) + makeDeleteRequest(request) + } + + def revokeUserAccessToViewWithoutOwnerAccess(bankId : String, accountId : String, userId : String, viewId : String) : h.HttpPackage[APIResponse]= { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId / "views" / viewId).DELETE.<@(consumer,token3) + makeDeleteRequest(request) + } + + def revokeUserAccessToAllViews(bankId : String, accountId : String, userId : String) : h.HttpPackage[APIResponse]= { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId / "views").DELETE.<@(consumer,token) + makeDeleteRequest(request) + } + + def revokeUserAccessToAllViewsWithoutOwnerAccess(bankId : String, accountId : String, userId : String) : h.HttpPackage[APIResponse]= { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / "permissions"/ userId / "views" ).DELETE.<@(consumer,token3) + makeDeleteRequest(request) + } + + def getTheOtherBankAccounts(bankId : String, accountId : String, viewId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" <@(consumer,token) + makeGetRequest(request) + } + + def getTheOtherBankAccountsWithoutToken(bankId : String, accountId : String, viewId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" + makeGetRequest(request) + } + + def getTheOtherBankAccountsWithWrongUser(bankId : String, accountId : String, viewId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" <@(consumer,token3) + makeGetRequest(request) + } + + def getTheOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId <@(consumer,token) + makeGetRequest(request) + } + + def getTheOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId + makeGetRequest(request) + } + + def getTheOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId <@(consumer,token3) + makeGetRequest(request) + } + + def getMetadataOfOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "metadata" <@(consumer,token) + makeGetRequest(request) + } + + def getMetadataOfOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "metadata" + makeGetRequest(request) + } + + def getMetadataOfOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "metadata" <@(consumer,token3) + makeGetRequest(request) + } + + def getThePublicAliasForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias" <@(consumer, token) + makeGetRequest(request) + } + + def getThePublicAliasForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias" + makeGetRequest(request) + } + + def getThePublicAliasForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias" <@(consumer, token3) + makeGetRequest(request) + } + + def postAPublicAliasForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias").POST <@(consumer,token) + val aliasJson = AliasJSON(alias) + makePostRequest(request, write(aliasJson)) + } + + def postAPublicAliasForAnOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias" + val aliasJson = AliasJSON(alias) + makePostRequest(request, write(aliasJson)) + } + + def postAPublicAliasForAnOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias").POST <@(consumer,token3) + val aliasJson = AliasJSON(alias) + makePostRequest(request, write(aliasJson)) + } + + def updateThePublicAliasForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias").PUT <@(consumer, token) + val aliasJson = AliasJSON(alias) + makePutRequest(request, write(aliasJson)) + } + + def updateThePublicAliasForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias" + val aliasJson = AliasJSON(alias) + makePutRequest(request, write(aliasJson)) + } + + def updateThePublicAliasForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias").PUT <@(consumer, token3) + val aliasJson = AliasJSON(alias) + makePutRequest(request, write(aliasJson)) + } + + def deleteThePublicAliasForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteThePublicAliasForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias" + makeDeleteRequest(request) + } + + def deleteThePublicAliasForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "public_alias").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getThePrivateAliasForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias" <@(consumer, token) + makeGetRequest(request) + } + + def getThePrivateAliasForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias" + makeGetRequest(request) + } + + def getThePrivateAliasForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias" <@(consumer, token3) + makeGetRequest(request) + } + + def postAPrivateAliasForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias").POST <@(consumer,token) + val aliasJson = AliasJSON(alias) + makePostRequest(request, write(aliasJson)) + } + + def postAPrivateAliasForAnOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias" + val aliasJson = AliasJSON(alias) + makePostRequest(request, write(aliasJson)) + } + + def postAPrivateAliasForAnOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias").POST <@(consumer,token3) + val aliasJson = AliasJSON(alias) + makePostRequest(request, write(aliasJson)) + } + + def updateThePrivateAliasForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias").PUT <@(consumer, token) + val aliasJson = AliasJSON(alias) + makePutRequest(request, write(aliasJson)) + } + + def updateThePrivateAliasForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias" + val aliasJson = AliasJSON(alias) + makePutRequest(request, write(aliasJson)) + } + + def updateThePrivateAliasForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, alias : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias").PUT <@(consumer, token3) + val aliasJson = AliasJSON(alias) + makePutRequest(request, write(aliasJson)) + } + + def deleteThePrivateAliasForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteThePrivateAliasForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias" + makeDeleteRequest(request) + } + + def deleteThePrivateAliasForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "private_alias").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getMoreInfoForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : String = { + getMetadataOfOneOtherBankAccount(bankId,accountId, viewId,otherBankAccountId).body.extract[OtherAccountMetadataJSON].more_info + } + + def postMoreInfoForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, moreInfo : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "more_info").POST <@(consumer,token) + val moreInfoJson = MoreInfoJSON(moreInfo) + makePostRequest(request, write(moreInfoJson)) + } + + def postMoreInfoForAnOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, moreInfo : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "more_info" + val moreInfoJson = MoreInfoJSON(moreInfo) + makePostRequest(request, write(moreInfoJson)) + } + + def postMoreInfoForAnOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, moreInfo : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "more_info").POST <@(consumer,token3) + val moreInfoJson = MoreInfoJSON(moreInfo) + makePostRequest(request, write(moreInfoJson)) + } + + def updateMoreInfoForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, moreInfo : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "more_info").PUT <@(consumer, token) + val moreInfoJson = MoreInfoJSON(moreInfo) + makePutRequest(request, write(moreInfoJson)) + } + + def updateMoreInfoForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, moreInfo : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "more_info" + val moreInfoJson = MoreInfoJSON(moreInfo) + makePutRequest(request, write(moreInfoJson)) + } + + def updateMoreInfoForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, moreInfo : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "more_info").PUT <@(consumer, token3) + val moreInfoJson = MoreInfoJSON(moreInfo) + makePutRequest(request, write(moreInfoJson)) + } + + def deleteMoreInfoForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "more_info").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteMoreInfoForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "more_info" + makeDeleteRequest(request) + } + + def deleteMoreInfoForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "more_info").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : String = { + getMetadataOfOneOtherBankAccount(bankId,accountId, viewId,otherBankAccountId).body.extract[OtherAccountMetadataJSON].URL + } + + def postUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, url : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "url").POST <@(consumer,token) + val urlJson = UrlJSON(url) + makePostRequest(request, write(urlJson)) + } + + def postUrlForAnOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, url : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "url" + val urlJson = UrlJSON(url) + makePostRequest(request, write(urlJson)) + } + + def postUrlForAnOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, url : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "url").POST <@(consumer,token3) + val urlJson = UrlJSON(url) + makePostRequest(request, write(urlJson)) + } + + def updateUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, url : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "url").PUT <@(consumer, token) + val urlJson = UrlJSON(url) + makePutRequest(request, write(urlJson)) + } + + def updateUrlForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, url : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "url" + val urlJson = UrlJSON(url) + makePutRequest(request, write(urlJson)) + } + + def updateUrlForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, url : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "url").PUT <@(consumer, token3) + val urlJson = UrlJSON(url) + makePutRequest(request, write(urlJson)) + } + + def deleteUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "url").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteUrlForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "url" + makeDeleteRequest(request) + } + + def deleteUrlForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "url").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getImageUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : String = { + getMetadataOfOneOtherBankAccount(bankId,accountId, viewId,otherBankAccountId).body.extract[OtherAccountMetadataJSON].image_URL + } + + def postImageUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, imageUrl : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "image_url").POST <@(consumer,token) + val imageUrlJson = ImageUrlJSON(imageUrl) + makePostRequest(request, write(imageUrlJson)) + } + + def postImageUrlForAnOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, imageUrl : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "image_url" + val imageUrlJson = ImageUrlJSON(imageUrl) + makePostRequest(request, write(imageUrlJson)) + } + + def postImageUrlForAnOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, imageUrl : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "image_url").POST <@(consumer,token3) + val imageUrlJson = ImageUrlJSON(imageUrl) + makePostRequest(request, write(imageUrlJson)) + } + + def updateImageUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, imageUrl : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "image_url").PUT <@(consumer, token) + val imageUrlJson = ImageUrlJSON(imageUrl) + makePutRequest(request, write(imageUrlJson)) + } + + def updateImageUrlForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, imageUrl : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "image_url" + val imageUrlJson = UrlJSON(imageUrl) + makePutRequest(request, write(imageUrlJson)) + } + + def updateImageUrlForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, imageUrl : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "image_url").PUT <@(consumer, token3) + val imageUrlJson = UrlJSON(imageUrl) + makePutRequest(request, write(imageUrlJson)) + } + + def deleteImageUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "image_url").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteImageUrlForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "image_url" + makeDeleteRequest(request) + } + + def deleteImageUrlForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "image_url").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getOpenCorporatesUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : String = { + getMetadataOfOneOtherBankAccount(bankId,accountId, viewId,otherBankAccountId).body.extract[OtherAccountMetadataJSON].open_corporates_URL + } + + def postOpenCorporatesUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, openCorporateUrl : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "open_corporates_url").POST <@(consumer,token) + val openCorporateUrlJson = OpenCorporateUrlJSON(openCorporateUrl) + makePostRequest(request, write(openCorporateUrlJson)) + } + + def postOpenCorporatesUrlForAnOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, openCorporateUrl : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "open_corporates_url" + val openCorporateUrlJson = OpenCorporateUrlJSON(openCorporateUrl) + makePostRequest(request, write(openCorporateUrlJson)) + } + + def postOpenCorporatesUrlForAnOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, openCorporateUrl : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "open_corporates_url").POST <@(consumer,token3) + val openCorporateUrlJson = OpenCorporateUrlJSON(openCorporateUrl) + makePostRequest(request, write(openCorporateUrlJson)) + } + + def updateOpenCorporatesUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, openCorporateUrl : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "open_corporates_url").PUT <@(consumer, token) + val openCorporateUrlJson = OpenCorporateUrlJSON(openCorporateUrl) + makePutRequest(request, write(openCorporateUrlJson)) + } + + def updateOpenCorporatesUrlForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, openCorporateUrl : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "open_corporates_url" + val openCorporateUrlJson = OpenCorporateUrlJSON(openCorporateUrl) + makePutRequest(request, write(openCorporateUrlJson)) + } + + def updateOpenCorporatesUrlForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, openCorporateUrl : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "open_corporates_url").PUT <@(consumer, token3) + val openCorporateUrlJson = OpenCorporateUrlJSON(openCorporateUrl) + makePutRequest(request, write(openCorporateUrlJson)) + } + + def deleteOpenCorporatesUrlForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "open_corporates_url").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteOpenCorporatesUrlForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "open_corporates_url" + makeDeleteRequest(request) + } + + def deleteOpenCorporatesUrlForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "open_corporates_url").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getCorporateLocationForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : LocationJSON = { + getMetadataOfOneOtherBankAccount(bankId,accountId, viewId,otherBankAccountId).body.extract[OtherAccountMetadataJSON].corporate_location + } + + def postCorporateLocationForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, corporateLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "corporate_location").POST <@(consumer,token) + val corpLocationJson = CorporateLocationJSON(corporateLocation) + makePostRequest(request, write(corpLocationJson)) + } + + def postCorporateLocationForAnOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, corporateLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "corporate_location" + val corpLocationJson = CorporateLocationJSON(corporateLocation) + makePostRequest(request, write(corpLocationJson)) + } + + def postCorporateLocationForAnOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, corporateLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "corporate_location").POST <@(consumer,token3) + val corpLocationJson = CorporateLocationJSON(corporateLocation) + makePostRequest(request, write(corpLocationJson)) + } + + def updateCorporateLocationForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, corporateLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "corporate_location").PUT <@(consumer, token) + val corpLocationJson = CorporateLocationJSON(corporateLocation) + makePutRequest(request, write(corpLocationJson)) + } + + def updateCorporateLocationForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, corporateLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "corporate_location" + val corpLocationJson = CorporateLocationJSON(corporateLocation) + makePutRequest(request, write(corpLocationJson)) + } + + def updateCorporateLocationForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, corporateLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "corporate_location").PUT <@(consumer, token3) + val corpLocationJson = CorporateLocationJSON(corporateLocation) + makePutRequest(request, write(corpLocationJson)) + } + + def deleteCorporateLocationForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "corporate_location").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteCorporateLocationForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "corporate_location" + makeDeleteRequest(request) + } + + def deleteCorporateLocationForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "corporate_location").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getPhysicalLocationForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : LocationJSON = { + getMetadataOfOneOtherBankAccount(bankId,accountId, viewId,otherBankAccountId).body.extract[OtherAccountMetadataJSON].physical_location + } + + def postPhysicalLocationForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, physicalLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "physical_location").POST <@(consumer,token) + val physLocationJson = PhysicalLocationJSON(physicalLocation) + makePostRequest(request, write(physLocationJson)) + } + + def postPhysicalLocationForAnOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, physicalLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "physical_location" + val physLocationJson = PhysicalLocationJSON(physicalLocation) + makePostRequest(request, write(physLocationJson)) + } + + def postPhysicalLocationForAnOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, physicalLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "physical_location").POST <@(consumer,token3) + val physLocationJson = PhysicalLocationJSON(physicalLocation) + makePostRequest(request, write(physLocationJson)) + } + + def updatePhysicalLocationForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, physicalLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "physical_location").PUT <@(consumer, token) + val physLocationJson = PhysicalLocationJSON(physicalLocation) + makePutRequest(request, write(physLocationJson)) + } + + def updatePhysicalLocationForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, physicalLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "physical_location" + val physLocationJson = PhysicalLocationJSON(physicalLocation) + makePutRequest(request, write(physLocationJson)) + } + + def updatePhysicalLocationForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String, physicalLocation : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "physical_location").PUT <@(consumer, token3) + val physLocationJson = PhysicalLocationJSON(physicalLocation) + makePutRequest(request, write(physLocationJson)) + } + + def deletePhysicalLocationForOneOtherBankAccount(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "physical_location").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deletePhysicalLocationForOneOtherBankAccountWithoutToken(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "physical_location" + makeDeleteRequest(request) + } + + def deletePhysicalLocationForOneOtherBankAccountWithWrongUser(bankId : String, accountId : String, viewId : String, otherBankAccountId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "other_accounts" / otherBankAccountId / "physical_location").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getTransactions(bankId : String, accountId : String, viewId : String): h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" <@(consumer,token) + makeGetRequest(request) + } + + def getTransaction(bankId : String, accountId : String, viewId : String, transactionId : String): h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "transaction" <@(consumer,token) + makeGetRequest(request) + } + + def getTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String): h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "transaction" + makeGetRequest(request) + } + + def getTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String): h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "transaction" <@(consumer,token3) + makeGetRequest(request) + } + + def getNarrativeForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative" <@(consumer, token) + makeGetRequest(request) + } + + def getNarrativeForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative" + makeGetRequest(request) + } + + def getNarrativeForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative" <@(consumer, token3) + makeGetRequest(request) + } + + def postNarrativeForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, narrative: String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative").POST <@(consumer,token) + val narrativeJson = TransactionNarrativeJSON(narrative) + makePostRequest(request, write(narrativeJson)) + } + + def postNarrativeForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, narrative: String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative" + val narrativeJson = TransactionNarrativeJSON(narrative) + makePostRequest(request, write(narrativeJson)) + } + + def postNarrativeForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, narrative: String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative").POST <@(consumer,token3) + val narrativeJson = TransactionNarrativeJSON(narrative) + makePostRequest(request, write(narrativeJson)) + } + + def updateNarrativeForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, narrative: String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative").PUT <@(consumer, token) + val narrativeJson = TransactionNarrativeJSON(narrative) + makePutRequest(request, write(narrativeJson)) + } + + def updateNarrativeForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, narrative: String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative" + val narrativeJson = TransactionNarrativeJSON(narrative) + makePutRequest(request, write(narrativeJson)) + } + + def updateNarrativeForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, narrative: String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative").PUT <@(consumer, token3) + val narrativeJson = TransactionNarrativeJSON(narrative) + makePutRequest(request, write(narrativeJson)) + } + + def deleteNarrativeForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteNarrativeForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative" + makeDeleteRequest(request) + } + + def deleteNarrativeForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "narrative").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getCommentsForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "comments" <@(consumer, token) + makeGetRequest(request) + } + + def getCommentsForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "comments" + makeGetRequest(request) + } + + def getCommentsForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "comments" <@(consumer, token3) + makeGetRequest(request) + } + + def postCommentForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, comment: PostTransactionCommentJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "comments").POST <@(consumer,token) + makePostRequest(request, write(comment)) + } + + def postCommentForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, comment: PostTransactionCommentJSON) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "comments" + makePostRequest(request, write(comment)) + } + + def postCommentForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, comment: PostTransactionCommentJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "comments").POST <@(consumer,token3) + makePostRequest(request, write(comment)) + } + + def deleteCommentForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, commentId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "comments" / commentId).DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteCommentForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, commentId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "comments" / commentId + makeDeleteRequest(request) + } + + def deleteCommentForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, commentId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "comments" / commentId).DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getTagsForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "tags" <@(consumer, token) + makeGetRequest(request) + } + + def getTagsForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "tags" + makeGetRequest(request) + } + + def getTagsForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "tags" <@(consumer, token3) + makeGetRequest(request) + } + + def postTagForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, tag: PostTransactionTagJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "tags").POST <@(consumer,token) + makePostRequest(request, write(tag)) + } + + def postTagForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, tag: PostTransactionTagJSON) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "tags" + makePostRequest(request, write(tag)) + } + + def postTagForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, tag: PostTransactionTagJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "tags").POST <@(consumer,token3) + makePostRequest(request, write(tag)) + } + + def deleteTagForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, tagId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "tags" / tagId).DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteTagForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, tagId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "tags" / tagId + makeDeleteRequest(request) + } + + def deleteTagForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, tagId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "tags" / tagId).DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getImagesForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "images" <@(consumer, token) + makeGetRequest(request) + } + + def getImagesForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "images" + makeGetRequest(request) + } + + def getImagesForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "images" <@(consumer, token3) + makeGetRequest(request) + } + + def postImageForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, image: PostTransactionImageJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "images").POST <@(consumer,token) + makePostRequest(request, write(image)) + } + + def postImageForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, image: PostTransactionImageJSON) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "images" + makePostRequest(request, write(image)) + } + + def postImageForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, image: PostTransactionImageJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "images").POST <@(consumer,token3) + makePostRequest(request, write(image)) + } + + def deleteImageForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, imageId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "images" / imageId).DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteImageForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, imageId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "images" / imageId + makeDeleteRequest(request) + } + + def deleteImageForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, imageId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "images" / imageId).DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getWhereForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where" <@(consumer, token) + makeGetRequest(request) + } + + def getWhereForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where" + makeGetRequest(request) + } + + def getWhereForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where" <@(consumer, token3) + makeGetRequest(request) + } + + def postWhereForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, where : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where").POST <@(consumer,token) + val whereJson = PostTransactionWhereJSON(where) + makePostRequest(request, write(whereJson)) + } + + def postWhereForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, where : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where" + val whereJson = PostTransactionWhereJSON(where) + makePostRequest(request, write(whereJson)) + } + + def postWhereForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, where : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where").POST <@(consumer,token3) + val whereJson = PostTransactionWhereJSON(where) + makePostRequest(request, write(whereJson)) + } + + def updateWhereForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String, where : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where").PUT <@(consumer, token) + val whereJson = PostTransactionWhereJSON(where) + makePutRequest(request, write(whereJson)) + } + + def updateWhereForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String, where : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where" + val whereJson = PostTransactionWhereJSON(where) + makePutRequest(request, write(whereJson)) + } + + def updateWhereForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String, where : LocationPlainJSON) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where").PUT <@(consumer, token3) + val whereJson = PostTransactionWhereJSON(where) + makePutRequest(request, write(whereJson)) + } + + def deleteWhereForOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where").DELETE <@(consumer, token) + makeDeleteRequest(request) + } + + def deleteWhereForOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where" + makeDeleteRequest(request) + } + + def deleteWhereForOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = (v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "metadata" / "where").DELETE <@(consumer, token3) + makeDeleteRequest(request) + } + + def getTheOtherBankAccountOfOneTransaction(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "other_account" <@(consumer, token) + makeGetRequest(request) + } + + def getTheOtherBankAccountOfOneTransactionWithoutToken(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "other_account" + makeGetRequest(request) + } + + def getTheOtherBankAccountOfOneTransactionWithWrongUser(bankId : String, accountId : String, viewId : String, transactionId : String) : h.HttpPackage[APIResponse] = { + val request = v1_2Request / "banks" / bankId / "accounts" / accountId / viewId / "transactions" / transactionId / "other_account" <@(consumer, token3) + makeGetRequest(request) + } + + +/************************ the tests ************************/ + feature("base line URL works"){ + scenario("we get the api information", API1_2, APIInfo) { + Given("We will not use an access token") + When("the request is sent") + val reply = getAPIInfo + Then("we should get a 200 ok code") + reply.code should equal (200) + val apiInfo = reply.body.extract[APIInfoJSON] + apiInfo.version should equal ("1.2") + apiInfo.git_commit.nonEmpty should equal (true) + } + } + + feature("Information about the hosted banks"){ + scenario("we get the hosted banks information", API1_2, GetHostedBanks) { + Given("We will not use an access token") + When("the request is sent") + val reply = getBanksInfo + Then("we should get a 200 ok code") + reply.code should equal (200) + val banksInfo = reply.body.extract[BanksJSON] + banksInfo.banks.foreach(b => { + b.id.nonEmpty should equal (true) + }) + } + } + + feature("Information about one hosted bank"){ + scenario("we get the hosted bank information", API1_2, GetHostedBank) { + Given("We will not use an access token") + When("the request is sent") + val reply = getBankInfo(randomBank) + Then("we should get a 200 ok code") + reply.code should equal (200) + val bankInfo = reply.body.extract[BankJSON] + bankInfo.id.nonEmpty should equal (true) + } + + scenario("we don't get the hosted bank information", API1_2, GetHostedBank) { + Given("We will not use an access token and request a random bankId") + When("the request is sent") + val reply = getBankInfo(randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("Information about all the bank accounts"){ + scenario("we get only the public bank accounts", API1_2, GetBankAccounts) { + Given("We will not use an access token") + When("the request is sent") + val reply = getBankAccounts(randomBank) + Then("we should get a 200 ok code") + reply.code should equal (200) + val publicAccountsInfo = reply.body.extract[AccountsJSON] + And("some fields should not be empty") + publicAccountsInfo.accounts.foreach(a => { + a.id.nonEmpty should equal (true) + a.views_available.nonEmpty should equal (true) + a.views_available.foreach( + //check that all the views are public + v => v.is_public should equal (true) + ) + }) + + } + scenario("we get the bank accounts the user have access to", API1_2, GetBankAccounts) { + Given("We will use an access token") + When("the request is sent") + val reply = getBankAccountsWithToken(randomBank) + Then("we should get a 200 ok code") + reply.code should equal (200) + val accountsInfo = reply.body.extract[AccountsJSON] + And("some fields should not be empty") + accountsInfo.accounts.foreach(a => { + a.id.nonEmpty should equal (true) + a.views_available.nonEmpty should equal (true) + }) + } + } + + feature("Information about the public bank accounts"){ + scenario("we get the public bank accounts", API1_2, GetPublicBankAccounts) { + Given("We will not use an access token") + When("the request is sent") + val reply = getPublicAccounts(randomBank) + Then("we should get a 200 ok code") + reply.code should equal (200) + val publicAccountsInfo = reply.body.extract[AccountsJSON] + And("some fields should not be empty") + publicAccountsInfo.accounts.foreach(a => { + a.id.nonEmpty should equal (true) + a.views_available.nonEmpty should equal (true) + a.views_available.foreach( + //check that all the views are public + v => v.is_public should equal (true) + ) + }) + } + } + + feature("Information about the private bank accounts"){ + scenario("we get the private bank accounts", API1_2, GetPrivateBankAccounts) { + Given("We will use an access token") + When("the request is sent") + val reply = getPrivateAccounts(randomBank) + Then("we should get a 200 ok code") + reply.code should equal (200) + And("some fields should not be empty") + val privateAccountsInfo = reply.body.extract[AccountsJSON] + privateAccountsInfo.accounts.foreach(a => { + a.id.nonEmpty should equal (true) + a.views_available.nonEmpty should equal (true) + }) + } + scenario("we don't get the private bank accounts", API1_2, GetPrivateBankAccounts) { + Given("We will not use an access token") + When("the request is sent") + val reply = getPrivateAccountsWithOutToken(randomBank) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("Information about a bank account"){ + scenario("we get data without using an access token", API1_2, GetBankAccount) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPublicAccount(bankId) + val randomPosition = nextInt(bankAccount.views_available.size) + val view = bankAccount.views_available.toList(randomPosition) + When("the request is sent") + val reply = getPublicBankAccountDetails(bankId, bankAccount.id, view.id) + Then("we should get a 200 ok code") + reply.code should equal (200) + And("some fields should not be empty") + val publicAccountDetails = reply.body.extract[ModeratedAccountJSON] + publicAccountDetails.id.nonEmpty should equal (true) + publicAccountDetails.bank_id.nonEmpty should equal (true) + publicAccountDetails.views_available.nonEmpty should equal (true) + } + + scenario("we get data by using an access token", API1_2, GetBankAccount) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val randomPosition = nextInt(bankAccount.views_available.size) + val view = bankAccount.views_available.toList(randomPosition) + When("the request is sent") + val reply = getPrivateBankAccountDetails(bankId, bankAccount.id, view.id) + Then("we should get a 200 ok code") + reply.code should equal (200) + val privateAccountDetails = reply.body.extract[ModeratedAccountJSON] + And("some fields should not be empty") + privateAccountDetails.id.nonEmpty should equal (true) + privateAccountDetails.bank_id.nonEmpty should equal (true) + privateAccountDetails.views_available.nonEmpty should equal (true) + } + } + + feature("List of the views of specific bank account"){ + scenario("We will get the list of the available views on a bank account", API1_2, GetViews) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getAccountViews(bankId, bankAccount.id) + Then("we should get a 200 ok code") + reply.code should equal (200) + reply.body.extract[ViewsJSON] + } + + scenario("We will not get the list of the available views on a bank account due to missing token", API1_2, GetViews) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getAccountViewsWithoutToken(bankId, bankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("We will not get the list of the available views on a bank account due to insufficient privileges", API1_2, GetViews) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getAccountViewsWithoutOwnerAccess(bankId, bankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("Information about the permissions of a specific bank account"){ + scenario("we will get one bank account permissions by using an access token", API1_2, GetPermissions) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getAccountPermissions(bankId, bankAccount.id) + Then("we should get a 200 ok code") + reply.code should equal (200) + reply.body.extract[PermissionsJSON] + } + + scenario("we will not get one bank account permissions", API1_2, GetPermissions) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getAccountPermissionsWithoutToken(bankId, bankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get one bank account permissions by using an other access token", API1_2, GetPermissions) { + Given("We will use an access token, but that does not grant owner view") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getAccountPermissionsWithoutOwnerAccess(bankId, bankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("Information about the permissions of a specific user on a specific bank account"){ + scenario("we will get the permissions by using an access token", API1_2, GetPermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val permission = randomAccountPermission(bankId, bankAccount.id) + val userID = urlEncode(permission.user.id) + When("the request is sent") + val reply = getUserAccountPermission(bankId, bankAccount.id, userID) + Then("we should get a 200 ok code") + reply.code should equal (200) + val viewsInfo = reply.body.extract[ViewsJSON] + And("some fields should not be empty") + viewsInfo.views.foreach(v => v.id.nonEmpty should equal (true)) + } + + scenario("we will not get the permissions of a specific user", API1_2, GetPermission) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val permission = randomAccountPermission(bankId, bankAccount.id) + val userID = urlEncode(permission.user.id) + When("the request is sent") + val reply = getUserAccountPermissionWithoutToken(bankId, bankAccount.id, userID) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the permissions of a random user", API1_2, GetPermission) { + Given("We will use an access token with random user id") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getUserAccountPermission(bankId, bankAccount.id, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("Grant a user access to a view on a bank account"){ + scenario("we will grant a user access to a view on an bank account", API1_2, PostPermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val userId = urlEncode(user2.email) + val reply = grantUserAccessToView(bankId, bankAccount.id, userId, randomViewPermalink) + Then("we should get a 201 ok code") + reply.code should equal (201) + val viewInfo = reply.body.extract[ViewJSON] + And("some fields should not be empty") + viewInfo.id.nonEmpty should equal (true) + } + + scenario("we cannot grant a user access to a view on an bank account because the user does not exist", API1_2, PostPermission) { + Given("We will use an access token with a random user Id") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = grantUserAccessToView(bankId, bankAccount.id, randomString(5), randomViewPermalink) + Then("we should get a 400 ok code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we cannot grant a user access to a view on an bank account because the view does not exist", API1_2, PostPermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user2.email) + When("the request is sent") + val reply = grantUserAccessToView(bankId, bankAccount.id, userId, randomString(5)) + Then("we should get a 400 ok code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we cannot grant a user access to a view on an bank account because the user does not have owner view access", API1_2, PostPermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user2.email) + When("the request is sent") + val reply = grantUserAccessToViewWithWrongUser(bankId, bankAccount.id, userId, randomViewPermalink) + Then("we should get a 400 ok code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("Grant a user access to a list of views on a bank account"){ + scenario("we will grant a user access to a list of views on an bank account", API1_2, PostPermissions) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user3.email) + val viewsIdsToGrant = randomViewsIdsToGrant(bankId, bankAccount.id) + When("the request is sent") + val reply = grantUserAccessToViews(bankId, bankAccount.id, userId, viewsIdsToGrant) + Then("we should get a 201 ok code") + reply.code should equal (201) + val viewsInfo = reply.body.extract[ViewsJSON] + And("some fields should not be empty") + viewsInfo.views.foreach(v => v.id.nonEmpty should equal (true)) + And("the granted views should be the same") + viewsIdsToGrant.toSet should equal(viewsInfo.views.map(_.id).toSet) + //we revoke access to the granted views for the next tests + revokeUserAccessToAllViews(bankId, bankAccount.id, userId) + } + + scenario("we cannot grant a user access to a list of views on an bank account because the user does not exist", API1_2, PostPermissions) { + Given("We will use an access token with a random user Id") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(randomString(5)) + val viewsIdsToGrant= randomViewsIdsToGrant(bankId, bankAccount.id) + When("the request is sent") + val reply = grantUserAccessToViews(bankId, bankAccount.id, userId, viewsIdsToGrant) + Then("we should get a 400 ok code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we cannot grant a user access to a list of views on an bank account because they don't exist", API1_2, PostPermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user3.email) + val viewsIdsToGrant= List(randomString(3),randomString(3)) + When("the request is sent") + val reply = grantUserAccessToViews(bankId, bankAccount.id, userId, viewsIdsToGrant) + Then("we should get a 400 ok code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we cannot grant a user access to a list of views on an bank account because some views don't exist", API1_2, PostPermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user3.email) + val viewsIdsToGrant= randomViewsIdsToGrant(bankId, bankAccount.id) ++ List(randomString(3),randomString(3)) + When("the request is sent") + val reply = grantUserAccessToViews(bankId, bankAccount.id, userId, viewsIdsToGrant) + Then("we should get a 400 ok code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we cannot grant a user access to a list of views on an bank account because the user does not have owner view access", API1_2, PostPermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user3.email) + val viewsIdsToGrant= randomViewsIdsToGrant(bankId, bankAccount.id) ++ List(randomString(3),randomString(3)) + When("the request is sent") + val reply = grantUserAccessToViewsWithWrongUser(bankId, bankAccount.id, userId, viewsIdsToGrant) + Then("we should get a 400 ok code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("Revoke a user access to a view on a bank account"){ + scenario("we will revoke the access of a user to a view on an bank account", API1_2, DeletePermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user2.email) + When("the request is sent") + val reply = revokeUserAccessToView(bankId, bankAccount.id, userId, randomViewPermalink) + Then("we should get a 204 no content code") + reply.code should equal (204) + } + + scenario("we cannot revoke the access to a user that does not exist", API1_2, DeletePermission) { + Given("We will use an access token with a random user Id") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = revokeUserAccessToView(bankId, bankAccount.id, randomString(5), randomViewPermalink) + Then("we should get a 400 ok code") + reply.code should equal (400) + } + + scenario("we cannot revoke a user access to a view on an bank account because the view does not exist", API1_2, DeletePermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user2.email) + When("the request is sent") + val reply = revokeUserAccessToView(bankId, bankAccount.id, userId, randomString(5)) + Then("we should get a 400 ok code") + reply.code should equal (400) + } + + scenario("we cannot revoke a user access to a view on an bank account because the user does not have owner view access", API1_2, DeletePermission) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user2.email) + When("the request is sent") + val reply = revokeUserAccessToViewWithoutOwnerAccess(bankId, bankAccount.id, userId, randomViewPermalink) + Then("we should get a 400 ok code") + reply.code should equal (400) + } + } + feature("Revoke a user access to all the views on a bank account"){ + scenario("we will revoke the access of a user to all the views on an bank account", API1_2, DeletePermissions) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user2.email) + When("the request is sent") + val reply = revokeUserAccessToAllViews(bankId, bankAccount.id, userId) + Then("we should get a 204 no content code") + reply.code should equal (204) + } + + scenario("we cannot revoke the access to a user that does not exist", API1_2, DeletePermissions) { + Given("We will use an access token with a random user Id") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = revokeUserAccessToAllViews(bankId, bankAccount.id, randomString(5)) + Then("we should get a 400 ok code") + reply.code should equal (400) + } + + scenario("we cannot revoke a user access to a view on an bank account because the user does not have owner view access", API1_2, DeletePermissions) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val userId = urlEncode(user2.email) + When("the request is sent") + val reply = revokeUserAccessToAllViewsWithoutOwnerAccess(bankId, bankAccount.id, userId) + Then("we should get a 400 ok code") + reply.code should equal (400) + } + } + + feature("We get the list of the other bank accounts linked with a bank account"){ + scenario("we will get the other bank accounts of a bank account", API1_2, GetOtherBankAccounts) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getTheOtherBankAccounts(bankId, bankAccount.id, randomViewPermalink) + Then("we should get a 200 code") + reply.code should equal (200) + val accountsJson = reply.body.extract[OtherAccountsJSON] + And("some fields should not be empty") + accountsJson.other_accounts.foreach( a => + a.id.nonEmpty should equal (true) + ) + } + + scenario("we will not get the other bank accounts of a bank account due to missing access token", API1_2, GetOtherBankAccounts) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getTheOtherBankAccountsWithoutToken(bankId, bankAccount.id, randomViewPermalink) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the other bank accounts of a bank account because the user does not have enough privileges", API1_2, GetOtherBankAccounts) { + Given("We will use an access token ") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getTheOtherBankAccountsWithWrongUser(bankId, bankAccount.id, randomViewPermalink) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the other bank accounts of a bank account because the view does not exist", API1_2, GetOtherBankAccounts) { + Given("We will use an access token ") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + When("the request is sent") + val reply = getTheOtherBankAccounts(bankId, bankAccount.id, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We get one specific other bank account among the other accounts "){ + scenario("we will get one random other bank account of a bank account", API1_2, GetOtherBankAccount) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTheOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 200 code") + reply.code should equal (200) + val accountJson = reply.body.extract[OtherAccountJSON] + And("some fields should not be empty") + accountJson.id.nonEmpty should equal (true) + } + + scenario("we will not get one random other bank account of a bank account due to a missing token", API1_2, GetOtherBankAccount) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTheOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get one random other bank account of a bank account because the user does not have enough privileges", API1_2, GetOtherBankAccount) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTheOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get one random other bank account of a bank account because the view does not exist", API1_2, GetOtherBankAccount) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, randomViewPermalink) + When("the request is sent") + val reply = getTheOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get one random other bank account of a bank account because the account does not exist", API1_2, GetOtherBankAccount) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getTheOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We get the metadata of one specific other bank account among the other accounts"){ + scenario("we will get the metadata of one random other bank account", API1_2, GetOtherBankAccountMetadata) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getMetadataOfOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 200 code") + reply.code should equal (200) + And("some fields should not be empty") + reply.body.extract[OtherAccountMetadataJSON] + } + + scenario("we will not get the metadata of one random other bank account due to a missing token", API1_2, GetOtherBankAccountMetadata) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getMetadataOfOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the metadata of one random other bank account because the user does not have enough privileges", API1_2, GetOtherBankAccountMetadata) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getMetadataOfOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the metadata of one random other bank account because the view does not exist", API1_2, GetOtherBankAccountMetadata) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getMetadataOfOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the metadata of one random other bank account because the account does not exist", API1_2, GetOtherBankAccountMetadata) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getMetadataOfOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We get the public alias of one specific other bank account among the other accounts "){ + scenario("we will get the public alias of one random other bank account", API1_2, GetPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 200 code") + reply.code should equal (200) + reply.body.extract[AliasJSON] + } + + scenario("we will not get the public alias of one random other bank account due to a missing token", API1_2, GetPublicAlias) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getThePublicAliasForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the public alias of one random other bank account because the user does not have enough privileges", API1_2, GetPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getThePublicAliasForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the public alias of one random other bank account because the view does not exist", API1_2, GetPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the public alias of one random other bank account because the account does not exist", API1_2, GetPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We post a public alias for one specific other bank"){ + scenario("we will post a public alias for one random other bank account", API1_2, PostPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomAlias = randomString(5) + val postReply = postAPublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the alias should be changed") + val getReply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should equal (theAliasAfterThePost.alias) + } + + scenario("we will not post a public alias for a random other bank account due to a missing token", API1_2, PostPublicAlias) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val postReply = postAPublicAliasForAnOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the alias should not be changed") + val getReply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should not equal (theAliasAfterThePost.alias) + } + + scenario("we will not post a public alias for a random other bank account because the user does not have enough privileges", API1_2, PostPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val postReply = postAPublicAliasForAnOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the alias should not be changed") + val getReply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should not equal (theAliasAfterThePost.alias) + } + + scenario("we will not post a public alias for a random other bank account because the view does not exist", API1_2, PostPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val postReply = postAPublicAliasForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the alias should not be changed") + val getReply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should not equal (theAliasAfterThePost.alias) + } + + scenario("we will not post a public alias for a random other bank account because the account does not exist", API1_2, PostPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomAlias = randomString(5) + When("the request is sent") + val postReply = postAPublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomAlias) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the public alias for one specific other bank"){ + scenario("we will update the public alias for one random other bank account", API1_2, PutPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomAlias = randomString(5) + val putReply = updateThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the alias should be changed") + val getReply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should equal (theAliasAfterThePost.alias) + } + + scenario("we will not update the public alias for a random other bank account due to a missing token", API1_2, PutPublicAlias) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val putReply = updateThePublicAliasForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the alias should not be changed") + val getReply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should not equal (theAliasAfterThePost.alias) + } + + scenario("we will not update the public alias for a random other bank account because the user does not have enough privileges", API1_2, PutPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val putReply = updateThePublicAliasForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the public alias for a random other bank account because the account does not exist", API1_2, PutPublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomAlias = randomString(5) + When("the request is sent") + val putReply = updateThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomAlias) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the public alias for one specific other bank"){ + scenario("we will delete the public alias for one random other bank account", API1_2, DeletePublicAlias) { + Given("We will use an access token and will set an alias first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + postAPublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + When("the delete request is sent") + val deleteReply = deleteThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the public alias should be null") + val getReply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterTheDelete : AliasJSON = getReply.body.extract[AliasJSON] + theAliasAfterTheDelete.alias should equal (null) + } + scenario("we will not delete the public alias for a random other bank account due to a missing token", API1_2, DeletePublicAlias) { + Given("We will not use an access token and will set an alias first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + postAPublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + When("the delete request is sent") + val deleteReply = deleteThePublicAliasForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the public alias should not be null") + val getReply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterTheDelete : AliasJSON = getReply.body.extract[AliasJSON] + theAliasAfterTheDelete.alias should not equal (null) + } + scenario("we will not delete the public alias for a random other bank account because the user does not have enough privileges", API1_2, DeletePublicAlias) { + Given("We will use an access token and will set an alias first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + postAPublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + When("the delete request is sent") + val deleteReply = deleteThePublicAliasForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the public alias should not be null") + val getReply = getThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterTheDelete : AliasJSON = getReply.body.extract[AliasJSON] + theAliasAfterTheDelete.alias should not equal (null) + } + scenario("we will not delete the public alias for a random other bank account because the account does not exist", API1_2, DeletePublicAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomAlias = randomString(5) + When("the delete request is sent") + val deleteReply = deleteThePublicAliasForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We get the private alias of one specific other bank account among the other accounts "){ + scenario("we will get the private alias of one random other bank account", API1_2, GetPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 200 code") + reply.code should equal (200) + reply.body.extract[AliasJSON] + } + + scenario("we will not get the private alias of one random other bank account due to a missing token", API1_2, GetPrivateAlias) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getThePrivateAliasForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the private alias of one random other bank account because the user does not have enough privileges", API1_2, GetPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getThePrivateAliasForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the private alias of one random other bank account because the view does not exist", API1_2, GetPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the private alias of one random other bank account because the account does not exist", API1_2, GetPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + + When("the request is sent") + val reply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We post a private alias for one specific other bank"){ + scenario("we will post a private alias for one random other bank account", API1_2, PostPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomAlias = randomString(5) + val postReply = postAPrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the alias should be changed") + val getReply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should equal (theAliasAfterThePost.alias) + } + + scenario("we will not post a private alias for a random other bank account due to a missing token", API1_2, PostPrivateAlias) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val postReply = postAPrivateAliasForAnOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the alias should not be changed") + val getReply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should not equal (theAliasAfterThePost.alias) + } + + scenario("we will not post a private alias for a random other bank account because the user does not have enough privileges", API1_2, PostPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val postReply = postAPrivateAliasForAnOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the alias should not be changed") + val getReply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should not equal (theAliasAfterThePost.alias) + } + + scenario("we will not post a private alias for a random other bank account because the view does not exist", API1_2, PostPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val postReply = postAPrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the alias should not be changed") + val getReply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should not equal (theAliasAfterThePost.alias) + } + + scenario("we will not post a private alias for a random other bank account because the account does not exist", API1_2, PostPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomAlias = randomString(5) + When("the request is sent") + val postReply = postAPrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomAlias) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the private alias for one specific other bank"){ + scenario("we will update the private alias for one random other bank account", API1_2, PutPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomAlias = randomString(5) + val putReply = updateThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the alias should be changed") + val getReply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should equal (theAliasAfterThePost.alias) + } + + scenario("we will not update the private alias for a random other bank account due to a missing token", API1_2, PutPrivateAlias) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val putReply = updateThePrivateAliasForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the alias should not be changed") + val getReply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterThePost : AliasJSON = getReply.body.extract[AliasJSON] + randomAlias should not equal (theAliasAfterThePost.alias) + } + + scenario("we will not update the private alias for a random other bank account because the user does not have enough privileges", API1_2, PutPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + When("the request is sent") + val putReply = updateThePrivateAliasForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the private alias for a random other bank account because the account does not exist", API1_2, PutPrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomAlias = randomString(5) + When("the request is sent") + val putReply = updateThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomAlias) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the private alias for one specific other bank"){ + scenario("we will delete the private alias for one random other bank account", API1_2, DeletePrivateAlias) { + Given("We will use an access token and will set an alias first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + postAPrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + When("the delete request is sent") + val deleteReply = deleteThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the Private alias should be null") + val getReply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterTheDelete : AliasJSON = getReply.body.extract[AliasJSON] + theAliasAfterTheDelete.alias should equal (null) + } + scenario("we will not delete the private alias for a random other bank account due to a missing token", API1_2, DeletePrivateAlias) { + Given("We will not use an access token and will set an alias first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + postAPrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + When("the delete request is sent") + val deleteReply = deleteThePrivateAliasForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the Private alias should not be null") + val getReply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterTheDelete : AliasJSON = getReply.body.extract[AliasJSON] + theAliasAfterTheDelete.alias should not equal (null) + } + scenario("we will not delete the private alias for a random other bank account because the user does not have enough privileges", API1_2, DeletePrivateAlias) { + Given("We will use an access token and will set an alias first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomAlias = randomString(5) + postAPrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomAlias) + When("the delete request is sent") + val deleteReply = deleteThePrivateAliasForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the Private alias should not be null") + val getReply = getThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + val theAliasAfterTheDelete : AliasJSON = getReply.body.extract[AliasJSON] + theAliasAfterTheDelete.alias should not equal (null) + } + scenario("we will not delete the private alias for a random other bank account because the account does not exist", API1_2, DeletePrivateAlias) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomAlias = randomString(5) + When("the delete request is sent") + val deleteReply = deleteThePrivateAliasForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We post more information for one specific other bank"){ + scenario("we will post more information for one random other bank account", API1_2, PostMoreInfo) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomInfo = randomString(20) + val postReply = postMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomInfo) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the information should be changed") + val moreInfo = getMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomInfo should equal (moreInfo) + } + + scenario("we will not post more information for a random other bank account due to a missing token", API1_2, PostMoreInfo) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomInfo = randomString(20) + When("the request is sent") + val postReply = postMoreInfoForAnOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomInfo) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the information should not be changed") + val moreInfo = getMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomInfo should not equal (moreInfo) + } + + scenario("we will not post more information for a random other bank account because the user does not have enough privileges", API1_2, PostMoreInfo) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomInfo = randomString(20) + When("the request is sent") + val postReply = postMoreInfoForAnOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomInfo) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the information should not be changed") + val moreInfo = getMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomInfo should not equal (moreInfo) + } + + scenario("we will not post more information for a random other bank account because the view does not exist", API1_2, PostMoreInfo) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomInfo = randomString(20) + When("the request is sent") + val postReply = postMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id, randomInfo) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the information should not be changed") + val moreInfo = getMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomInfo should not equal (moreInfo) + } + + scenario("we will not post more information for a random other bank account because the account does not exist", API1_2, PostMoreInfo) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomInfo = randomString(20) + When("the request is sent") + val postReply = postMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomInfo) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the information for one specific other bank"){ + scenario("we will update the information for one random other bank account", API1_2, PutMoreInfo) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomInfo = randomString(20) + val putReply = updateMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomInfo) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the information should be changed") + val moreInfo = getMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomInfo should equal (moreInfo) + } + + scenario("we will not update the information for a random other bank account due to a missing token", API1_2, PutMoreInfo) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomInfo = randomString(20) + When("the request is sent") + val putReply = updateMoreInfoForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomInfo) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the information should not be changed") + val moreInfo = getMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomInfo should not equal (moreInfo) + } + + scenario("we will not update the information for a random other bank account because the user does not have enough privileges", API1_2, PutMoreInfo) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomInfo = randomString(20) + When("the request is sent") + val putReply = updateMoreInfoForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomInfo) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the information for a random other bank account because the account does not exist", API1_2, PutMoreInfo) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomInfo = randomString(20) + When("the request is sent") + val putReply = updateMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomInfo) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the information for one specific other bank"){ + scenario("we will delete the information for one random other bank account", API1_2, DeleteMoreInfo) { + Given("We will use an access token and will set an info first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomInfo = randomString(20) + postMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomInfo) + When("the delete request is sent") + val deleteReply = deleteMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the info should be null") + val infoAfterDelete = getMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + infoAfterDelete should equal (null) + } + + scenario("we will not delete the information for a random other bank account due to a missing token", API1_2, DeleteMoreInfo) { + Given("We will not use an access token and will set an info first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomInfo = randomString(20) + postMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomInfo) + When("the delete request is sent") + val deleteReply = deleteMoreInfoForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the info should not be null") + val infoAfterDelete = getMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + infoAfterDelete should not equal (null) + } + + scenario("we will not delete the information for a random other bank account because the user does not have enough privileges", API1_2, DeleteMoreInfo) { + Given("We will use an access token and will set an info first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomInfo = randomString(20) + postMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomInfo) + When("the delete request is sent") + val deleteReply = deleteMoreInfoForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the info should not be null") + val infoAfterDelete = getMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + infoAfterDelete should not equal (null) + } + + scenario("we will not delete the information for a random other bank account because the account does not exist", API1_2, DeleteMoreInfo) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomInfo = randomString(20) + When("the delete request is sent") + val deleteReply = deleteMoreInfoForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We post the url for one specific other bank"){ + scenario("we will post the url for one random other bank account", API1_2, PostURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomURL = randomString(20) + val postReply = postUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the url should be changed") + val url = getUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should equal (url) + } + + scenario("we will not post the url for a random other bank account due to a missing token", API1_2, PostURL) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val postReply = postUrlForAnOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the url should not be changed") + val url = getUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should not equal (url) + } + + scenario("we will not post the url for a random other bank account because the user does not have enough privileges", API1_2, PostURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val postReply = postUrlForAnOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the url should not be changed") + val url = getUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should not equal (url) + } + + scenario("we will not post the url for a random other bank account because the view does not exist", API1_2, PostURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val postReply = postUrlForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id, randomURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the url should not be changed") + val url = getUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should not equal (url) + } + + scenario("we will not post the url for a random other bank account because the account does not exist", API1_2, PostURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomURL = randomString(20) + When("the request is sent") + val postReply = postUrlForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the url for one specific other bank"){ + scenario("we will update the url for one random other bank account", API1_2, PutURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomURL = randomString(20) + val putReply = updateUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the url should be changed") + val url = getUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should equal (url) + } + + scenario("we will not update the url for a random other bank account due to a missing token", API1_2, PutURL) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val putReply = updateUrlForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the url should not be changed") + val url = getUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should not equal (url) + } + + scenario("we will not update the url for a random other bank account because the user does not have enough privileges", API1_2, PutURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val putReply = updateUrlForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the url for a random other bank account because the account does not exist", API1_2, PutURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomURL = randomString(20) + When("the request is sent") + val putReply = updateUrlForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomURL) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the url for one specific other bank"){ + scenario("we will delete the url for one random other bank account", API1_2, DeleteURL) { + Given("We will use an access token and will set an open corporates url first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + postUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + When("the delete request is sent") + val deleteReply = deleteUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the url should be null") + val urlAfterDelete = getUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + urlAfterDelete should equal (null) + } + + scenario("we will not delete the url for a random other bank account due to a missing token", API1_2, DeleteURL) { + Given("We will not use an access token and will set an open corporates url first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + postUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + When("the delete request is sent") + val deleteReply = deleteUrlForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the url should not be null") + val urlAfterDelete = getUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + urlAfterDelete should not equal (null) + } + + scenario("we will not delete the url for a random other bank account because the user does not have enough privileges", API1_2, DeleteURL) { + Given("We will use an access token and will set an open corporates url first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + postUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + When("the delete request is sent") + val deleteReply = deleteUrlForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the url should not be null") + val urlAfterDelete = getUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + urlAfterDelete should not equal (null) + } + + scenario("we will not delete the url for a random other bank account because the account does not exist", API1_2, DeleteURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomURL = randomString(20) + When("the delete request is sent") + val deleteReply = deleteUrlForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We post the image url for one specific other bank"){ + scenario("we will post the image url for one random other bank account", API1_2, PostImageURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomImageURL = randomString(20) + val postReply = postImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomImageURL) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the image url should be changed") + val url = getImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomImageURL should equal (url) + } + + scenario("we will not post the image url for a random other bank account due to a missing token", API1_2, PostImageURL) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomImageURL = randomString(20) + When("the request is sent") + val postReply = postImageUrlForAnOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomImageURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the image url should not be changed") + val url = getImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomImageURL should not equal (url) + } + + scenario("we will not post the image url for a random other bank account because the user does not have enough privileges", API1_2, PostImageURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomImageURL = randomString(20) + When("the request is sent") + val postReply = postImageUrlForAnOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomImageURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the image url should not be changed") + val url = getImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomImageURL should not equal (url) + } + + scenario("we will not post the image url for a random other bank account because the view does not exist", API1_2, PostImageURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomImageURL = randomString(20) + When("the request is sent") + val postReply = postImageUrlForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id, randomImageURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the image url should not be changed") + val url = getImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomImageURL should not equal (url) + } + + scenario("we will not post the image url for a random other bank account because the account does not exist", API1_2, PostImageURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomImageURL = randomString(20) + When("the request is sent") + val postReply = postImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomImageURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the image url for one specific other bank"){ + scenario("we will update the image url for one random other bank account", API1_2, PutImageURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomImageURL = randomString(20) + val putReply = updateImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomImageURL) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the image url should be changed") + val url = getImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomImageURL should equal (url) + } + + scenario("we will not update the image url for a random other bank account due to a missing token", API1_2, PutImageURL) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomImageURL = randomString(20) + When("the request is sent") + val putReply = updateImageUrlForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomImageURL) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the image url should not be changed") + val url = getImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomImageURL should not equal (url) + } + + scenario("we will not update the image url for a random other bank account because the user does not have enough privileges", API1_2, PutImageURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomImageURL = randomString(20) + When("the request is sent") + val putReply = updateImageUrlForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomImageURL) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the image url for a random other bank account because the account does not exist", API1_2, PutImageURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomImageURL = randomString(20) + When("the request is sent") + val putReply = updateImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomImageURL) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the image url for one specific other bank"){ + scenario("we will delete the image url for one random other bank account", API1_2, DeleteImageURL) { + Given("We will use an access token and will set a url first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomImageURL = randomString(20) + postImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomImageURL) + When("the delete request is sent") + val deleteReply = deleteImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the image url should be null") + val urlAfterDelete = getImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + urlAfterDelete should equal (null) + } + + scenario("we will not delete the image url for a random other bank account due to a missing token", API1_2, DeleteImageURL) { + Given("We will not use an access token and will set a url first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomImageURL = randomString(20) + postImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomImageURL) + When("the delete request is sent") + val deleteReply = deleteImageUrlForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the image url should not be null") + val urlAfterDelete = getImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + urlAfterDelete should not equal (null) + } + + scenario("we will not delete the image url for a random other bank account because the user does not have enough privileges", API1_2, DeleteImageURL) { + Given("We will use an access token and will set a url first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomImageURL = randomString(20) + postImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomImageURL) + When("the delete request is sent") + val deleteReply = deleteImageUrlForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the image url should not be null") + val urlAfterDelete = getImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + urlAfterDelete should not equal (null) + } + + scenario("we will not delete the image url for a random other bank account because the account does not exist", API1_2, DeleteImageURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomImageURL = randomString(20) + When("the delete request is sent") + val deleteReply = deleteImageUrlForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We post the open corporates url for one specific other bank"){ + scenario("we will post the open corporates url for one random other bank account", API1_2, PostOpenCorporatesURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomURL = randomString(20) + val postReply = postOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the open corporates url should be changed") + val url = getOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should equal (url) + } + + scenario("we will not post the open corporates url for a random other bank account due to a missing token", API1_2, PostOpenCorporatesURL) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val postReply = postOpenCorporatesUrlForAnOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the open corporates url should not be changed") + val url = getOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should not equal (url) + } + + scenario("we will not post the open corporates url for a random other bank account because the user does not have enough privileges", API1_2, PostOpenCorporatesURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val postReply = postOpenCorporatesUrlForAnOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the open corporates url should not be changed") + val url = getOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should not equal (url) + } + + scenario("we will not post the open corporates url for a random other bank account because the view does not exist", API1_2, PostOpenCorporatesURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val postReply = postOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id, randomURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the open corporates url should not be changed") + val url = getOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should not equal (url) + } + + scenario("we will not post the open corporates url for a random other bank account because the account does not exist", API1_2, PostOpenCorporatesURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomURL = randomString(20) + When("the request is sent") + val postReply = postOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomURL) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the open corporates url for one specific other bank"){ + scenario("we will update the open corporates url for one random other bank account", API1_2, PutOpenCorporatesURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomURL = randomString(20) + val putReply = updateOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the open corporates url should be changed") + val url = getOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should equal (url) + } + + scenario("we will not update the open corporates url for a random other bank account due to a missing token", API1_2, PutOpenCorporatesURL) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val putReply = updateOpenCorporatesUrlForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the open corporates url should not be changed") + val url = getOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomURL should not equal (url) + } + + scenario("we will not update the open corporates url for a random other bank account because the user does not have enough privileges", API1_2, PutOpenCorporatesURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + When("the request is sent") + val putReply = updateOpenCorporatesUrlForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the open corporates url for a random other bank account because the account does not exist", API1_2, PutOpenCorporatesURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomURL = randomString(20) + When("the request is sent") + val putReply = updateOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomURL) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the open corporates url for one specific other bank"){ + scenario("we will delete the open corporates url for one random other bank account", API1_2, DeleteOpenCorporatesURL) { + Given("We will use an access token and will set an open corporates url first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + postOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + When("the delete request is sent") + val deleteReply = deleteOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the open corporates url should be null") + val urlAfterDelete = getOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + urlAfterDelete should equal (null) + } + + scenario("we will not delete the open corporates url for a random other bank account due to a missing token", API1_2, DeleteOpenCorporatesURL) { + Given("We will not use an access token and will set an open corporates url first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + postOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + When("the delete request is sent") + val deleteReply = deleteOpenCorporatesUrlForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the open corporates url should not be null") + val urlAfterDelete = getOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + urlAfterDelete should not equal (null) + } + + scenario("we will not delete the open corporates url for a random other bank account because the user does not have enough privileges", API1_2, DeleteOpenCorporatesURL) { + Given("We will use an access token and will set an open corporates url first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomURL = randomString(20) + postOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomURL) + When("the delete request is sent") + val deleteReply = deleteOpenCorporatesUrlForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the open corporates url should not be null") + val urlAfterDelete = getOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + urlAfterDelete should not equal (null) + } + + scenario("we will not delete the open corporates url for a random other bank account because the account does not exist", API1_2, DeleteOpenCorporatesURL) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomURL = randomString(20) + When("the delete request is sent") + val deleteReply = deleteOpenCorporatesUrlForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We post the corporate location for one specific other bank"){ + scenario("we will post the corporate location for one random other bank account", API1_2, PostCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the corporate location should be changed") + val location = getCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomLoc.latitude should equal (location.latitude) + randomLoc.longitude should equal (location.longitude) + } + + scenario("we will not post the corporate location for a random other bank account due to a missing token", API1_2, PostCorporateLocation) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postCorporateLocationForAnOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the corporate location for one random other bank account because the coordinates don't exist", API1_2, PostCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + var randomLoc = JSONFactory.createLocationPlainJSON(400,200) + When("the request is sent") + val postReply = postCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the corporate location for a random other bank account because the user does not have enough privileges", API1_2, PostCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postCorporateLocationForAnOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the corporate location for a random other bank account because the view does not exist", API1_2, PostCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the corporate location for a random other bank account because the account does not exist", API1_2, PostCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomLoc = randomLocation + When("the request is sent") + val postReply = postCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the corporate location for one specific other bank"){ + scenario("we will update the corporate location for one random other bank account", API1_2, PutCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomLoc = randomLocation + val putReply = updateCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the corporate location should be changed") + val location = getCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomLoc.latitude should equal (location.latitude) + randomLoc.longitude should equal (location.longitude) + } + + scenario("we will not update the corporate location for one random other bank account because the coordinates don't exist", API1_2, PutCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + var randomLoc = JSONFactory.createLocationPlainJSON(400,200) + When("the request is sent") + val putReply = updateCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the corporate location for a random other bank account due to a missing token", API1_2, PutCorporateLocation) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val putReply = updateCorporateLocationForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the corporate location for a random other bank account because the user does not have enough privileges", API1_2, PutCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val putReply = updateCorporateLocationForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the corporate location for a random other bank account because the account does not exist", API1_2, PutCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomLoc = randomLocation + When("the request is sent") + val putReply = updateCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the corporate location for one specific other bank"){ + scenario("we will delete the corporate location for one random other bank account", API1_2, DeleteCorporateLocation) { + Given("We will use an access token and will set a corporate location first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + When("the delete request is sent") + val deleteReply = deleteCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the corporate location should be null") + val locationAfterDelete = getCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + locationAfterDelete should equal (null) + } + + scenario("we will not delete the corporate location for a random other bank account due to a missing token", API1_2, DeleteCorporateLocation) { + Given("We will not use an access token and will set a corporate location first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + When("the delete request is sent") + val deleteReply = deleteCorporateLocationForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the corporate location should not be null") + val locationAfterDelete = getCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + locationAfterDelete should not equal (null) + } + + scenario("we will not delete the corporate location for a random other bank account because the user does not have enough privileges", API1_2, DeleteCorporateLocation) { + Given("We will use an access token and will set a corporate location first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + When("the delete request is sent") + val deleteReply = deleteCorporateLocationForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the corporate location should not be null") + val locationAfterDelete = getCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + locationAfterDelete should not equal (null) + } + + scenario("we will not delete the corporate location for a random other bank account because the account does not exist", API1_2, DeleteCorporateLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomLoc = randomLocation + When("the delete request is sent") + val deleteReply = deleteCorporateLocationForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We post the physical location for one specific other bank"){ + scenario("we will post the physical location for one random other bank account", API1_2, PostPhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the physical location should be changed") + val location = getPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomLoc.latitude should equal (location.latitude) + randomLoc.longitude should equal (location.longitude) + } + + scenario("we will not post the physical location for one random other bank account because the coordinates don't exist", API1_2, PostPhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + var randomLoc = JSONFactory.createLocationPlainJSON(400,200) + When("the request is sent") + val postReply = postPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the physical location for a random other bank account due to a missing token", API1_2, PostPhysicalLocation) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postPhysicalLocationForAnOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the physical location for a random other bank account because the user does not have enough privileges", API1_2, PostPhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postPhysicalLocationForAnOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the physical location for a random other bank account because the view does not exist", API1_2, PostPhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, randomString(5), otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the physical location for a random other bank account because the account does not exist", API1_2, PostPhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomLoc = randomLocation + When("the request is sent") + val postReply = postPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the physical location for one specific other bank"){ + scenario("we will update the physical location for one random other bank account", API1_2, PutPhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + When("the request is sent") + val randomLoc = randomLocation + val putReply = updatePhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the physical location should be changed") + val location = getPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + randomLoc.latitude should equal (location.latitude) + randomLoc.longitude should equal (location.longitude) + } + + scenario("we will not update the physical location for one random other bank account because the coordinates don't exist", API1_2, PutPhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + var randomLoc = JSONFactory.createLocationPlainJSON(400,200) + When("the request is sent") + val putReply = updatePhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the physical location for a random other bank account due to a missing token", API1_2, PutPhysicalLocation) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val putReply = updatePhysicalLocationForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the physical location for a random other bank account because the user does not have enough privileges", API1_2, PutPhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val putReply = updatePhysicalLocationForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the physical location for a random other bank account because the account does not exist", API1_2, PutPhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomLoc = randomLocation + When("the request is sent") + val putReply = updatePhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5), randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the physical location for one specific other bank"){ + scenario("we will delete the physical location for one random other bank account", API1_2, DeletePhysicalLocation) { + Given("We will use an access token and will set a physical location first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + When("the delete request is sent") + val deleteReply = deletePhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the physical location should be null") + val locationAfterDelete = getPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + locationAfterDelete should equal (null) + } + + scenario("we will not delete the physical location for a random other bank account due to a missing token", API1_2, DeletePhysicalLocation) { + Given("We will not use an access token and will set a physical location first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + When("the delete request is sent") + val deleteReply = deletePhysicalLocationForOneOtherBankAccountWithoutToken(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the physical location should not be null") + val locationAfterDelete = getPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + locationAfterDelete should not equal (null) + } + + scenario("we will not delete the physical location for a random other bank account because the user does not have enough privileges", API1_2, DeletePhysicalLocation) { + Given("We will use an access token and will set a physical location first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val otherBankAccount = randomOtherBankAccount(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id, randomLoc) + When("the delete request is sent") + val deleteReply = deletePhysicalLocationForOneOtherBankAccountWithWrongUser(bankId, bankAccount.id, view, otherBankAccount.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the physical location should not be null") + val locationAfterDelete = getPhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, otherBankAccount.id) + locationAfterDelete should not equal (null) + } + + scenario("we will not delete the physical location for a random other bank account because the account does not exist", API1_2, DeletePhysicalLocation) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val randomLoc = randomLocation + When("the delete request is sent") + val deleteReply = deletePhysicalLocationForOneOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("Information about all the transaction"){ + scenario("we get all the transactions of one random (private) bank account", API1_2, GetTransactions) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getTransactions(bankId,bankAccount.id,view) + Then("we should get a 200 ok code") + reply.code should equal (200) + val transactions = reply.body.extract[TransactionsJSON] + } + + scenario("we do not get transactions of one random bank account, because the account doesn't exist", API1_2, GetTransactions) { + Given("We will use an access token") + When("the request is sent") + val bankId = randomBank + val view = randomViewPermalink + val reply = getTransactions(bankId,randomString(5),view) + Then("we should get a 400 code") + reply.code should equal (400) + } + + scenario("we do not get transactions of one random bank account, because the view doesn't exist", API1_2, GetTransactions) { + Given("We will use an access token") + When("the request is sent") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val reply = getTransactions(bankId,bankAccount.id,randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + } + } + + feature("Information about a transaction"){ + scenario("we get transaction data by using an access token", API1_2, GetTransaction) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTransaction(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 200 ok code") + reply.code should equal (200) + reply.body.extract[TransactionJSON] + } + + scenario("we will not get transaction data due to a missing token", API1_2, GetTransaction) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + } + + scenario("we will not get transaction data because user does not have enough privileges", API1_2, GetTransaction) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + } + + scenario("we will not get transaction data because the account does not exist", API1_2, GetTransaction) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTransactionWithoutToken(bankId, randomString(5), view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + } + + scenario("we will not get transaction data because the view does not exist", API1_2, GetTransaction) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTransactionWithoutToken(bankId, bankAccount.id, randomString(5), transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + } + + scenario("we will not get transaction data because the transaction does not exist", API1_2, GetTransaction) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getTransactionWithoutToken(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + } + + } + + feature("We get the narrative of one random transaction"){ + scenario("we will get the narrative of one random transaction", API1_2, GetNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 200 code") + reply.code should equal (200) + reply.body.extract[TransactionNarrativeJSON] + } + + scenario("we will not get the narrative of one random transaction due to a missing token", API1_2, GetNarrative) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getNarrativeForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the narrative of one random transaction because the user does not have enough privileges", API1_2, GetNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getNarrativeForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the narrative of one random transaction because the view does not exist", API1_2, GetNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getNarrativeForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the narrative of one random transaction because the transaction does not exist", API1_2, GetNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We post the narrative for one random transaction"){ + scenario("we will post the narrative for one random transaction", API1_2, PostNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val randomNarrative = randomString(20) + val postReply = postNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomNarrative) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the narrative should be added") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theNarrativeAfterThePost : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + randomNarrative should equal (theNarrativeAfterThePost.narrative) + } + + scenario("we will not post the narrative for one random transaction due to a missing token", API1_2, PostNarrative) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomNarrative = randomString(20) + When("the request is sent") + val postReply = postNarrativeForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, randomNarrative) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the narrative should not be added") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theNarrativeAfterThePost : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + randomNarrative should not equal (theNarrativeAfterThePost.narrative) + } + + scenario("we will not post the narrative for one random transaction because the user does not have enough privileges", API1_2, PostNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomNarrative = randomString(20) + When("the request is sent") + val postReply = postNarrativeForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, randomNarrative) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the narrative should not be added") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theNarrativeAfterThePost : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + randomNarrative should not equal (theNarrativeAfterThePost.narrative) + } + + scenario("we will not post the narrative for one random transaction because the view does not exist", API1_2, PostNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomNarrative = randomString(20) + When("the request is sent") + val postReply = postNarrativeForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id, randomNarrative) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the narrative should not be added") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theNarrativeAfterThePost : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + randomNarrative should not equal (theNarrativeAfterThePost.narrative) + } + + scenario("we will not post the narrative for one random transaction because the transaction does not exist", API1_2, PostNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val randomNarrative = randomString(20) + When("the request is sent") + val postReply = postNarrativeForOneTransaction(bankId, bankAccount.id, view, randomString(5), randomNarrative) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the narrative for one random transaction"){ + scenario("we will the narrative for one random transaction", API1_2, PutNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val randomNarrative = randomString(20) + val putReply = updateNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomNarrative) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the narrative should be changed") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val narrativeAfterThePost : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + randomNarrative should equal (narrativeAfterThePost.narrative) + } + + scenario("we will not update the narrative for one random transaction due to a missing token", API1_2, PutNarrative) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomNarrative = randomString(20) + When("the request is sent") + val putReply = updateNarrativeForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, randomNarrative) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the narrative should not be changed") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val narrativeAfterThePost : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + randomNarrative should not equal (narrativeAfterThePost.narrative) + } + + scenario("we will not update the narrative for one random transaction because the user does not have enough privileges", API1_2, PutNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomNarrative = randomString(20) + When("the request is sent") + val putReply = updateNarrativeForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, randomNarrative) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the narrative should not be changed") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val narrativeAfterThePost : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + randomNarrative should not equal (narrativeAfterThePost.narrative) + } + + scenario("we will not update the narrative for one random transaction because the transaction does not exist", API1_2, PutNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transactionId = randomString(5) + val randomNarrative = randomString(20) + When("the request is sent") + val putReply = updateNarrativeForOneTransaction(bankId, bankAccount.id, view, transactionId, randomNarrative) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the narrative for one random transaction"){ + scenario("we will delete the narrative for one random transaction", API1_2, DeleteNarrative) { + Given("We will use an access token and will set a narrative first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomNarrative = randomString(20) + postNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomNarrative) + When("the delete request is sent") + val deleteReply = deleteNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the narrative should be null") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val narrativeAfterTheDelete : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + narrativeAfterTheDelete.narrative should equal (null) + } + + scenario("we will not delete narrative for one random transaction due to a missing token", API1_2, DeleteNarrative) { + Given("We will not use an access token and will set a narrative first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomNarrative = randomString(20) + postNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomNarrative) + When("the delete request is sent") + val deleteReply = deleteNarrativeForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the public narrative should not be null") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val narrativeAfterTheDelete : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + narrativeAfterTheDelete.narrative should not equal (null) + } + + scenario("we will not delete the narrative for one random transaction because the user does not have enough privileges", API1_2, DeleteNarrative) { + Given("We will use an access token and will set a narrative first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomNarrative = randomString(20) + postNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomNarrative) + When("the delete request is sent") + val deleteReply = deleteNarrativeForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + And("the narrative should not be null") + val getReply = getNarrativeForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val narrativeAfterTheDelete : TransactionNarrativeJSON = getReply.body.extract[TransactionNarrativeJSON] + narrativeAfterTheDelete.narrative should not equal (null) + } + + scenario("we will not delete the narrative for one random transaction because the transaction does not exist", API1_2, DeleteNarrative) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "owner" + val randomNarrative = randomString(20) + When("the delete request is sent") + val deleteReply = deleteNarrativeForOneTransaction(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We get the comments of one random transaction"){ + scenario("we will get the comments of one random transaction", API1_2, GetComments) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getCommentsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 200 code") + reply.code should equal (200) + reply.body.extract[TransactionCommentsJSON] + } + + scenario("we will not get the comments of one random transaction due to a missing token", API1_2, GetComments) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getCommentsForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the comments of one random transaction because the user does not have enough privileges", API1_2, GetComments) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getCommentsForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the comments of one random transaction because the view does not exist", API1_2, GetComments) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getCommentsForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the comments of one random transaction because the transaction does not exist", API1_2, GetComments) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getCommentsForOneTransaction(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We post a comment for one random transaction"){ + scenario("we will post a comment for one random transaction", API1_2, PostComment) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val randomComment = PostTransactionCommentJSON(randomString(20)) + val postReply = postCommentForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomComment) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[TransactionCommentJSON] + And("the comment should be added") + val getReply = getCommentsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theCommentsAfterThePost = getReply.body.extract[TransactionCommentsJSON].comments + val theComment = theCommentsAfterThePost.find(_.value == randomComment.value) + theComment.nonEmpty should equal (true) + theComment.get.user should not equal (null) + + } + + scenario("we will not post a comment for one random transaction due to a missing token", API1_2, PostComment) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomComment = PostTransactionCommentJSON(randomString(20)) + When("the request is sent") + val postReply = postCommentForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, randomComment) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the comment should not be added") + val getReply = getCommentsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theCommentsAfterThePost = getReply.body.extract[TransactionCommentsJSON].comments + val notFound = theCommentsAfterThePost.find(_.value == randomComment.value) match { + case None => true + case Some(_) => false + } + notFound should equal (true) + } + + + scenario("we will not post a comment for one random transaction because the user does not have enough privileges", API1_2, PostComment) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomComment = PostTransactionCommentJSON(randomString(20)) + When("the request is sent") + val postReply = postCommentForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, randomComment) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the comment should not be added") + val getReply = getCommentsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theCommentsAfterThePost = getReply.body.extract[TransactionCommentsJSON].comments + val notFound = theCommentsAfterThePost.find(_.value == randomComment.value) match { + case None => true + case Some(_) => false + } + notFound should equal (true) + } + + scenario("we will not post a comment for one random transaction because the view does not exist", API1_2, PostComment) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomComment = PostTransactionCommentJSON(randomString(20)) + When("the request is sent") + val postReply = postCommentForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id, randomComment) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the comment should not be added") + val getReply = getCommentsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theCommentsAfterThePost = getReply.body.extract[TransactionCommentsJSON].comments + val notFound = theCommentsAfterThePost.find(_.value == randomComment.value) match { + case None => true + case Some(_) => false + } + notFound should equal (true) + } + + scenario("we will not post a comment for one random transaction because the transaction does not exist", API1_2, PostComment) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomComment = PostTransactionCommentJSON(randomString(20)) + When("the request is sent") + val postReply = postCommentForOneTransaction(bankId, bankAccount.id, view, randomString(5), randomComment) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete a comment for one random transaction"){ + scenario("we will delete a comment for one random transaction", API1_2, DeleteComment) { + Given("We will use an access token and will set a comment first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomComment = PostTransactionCommentJSON(randomString(20)) + val postedReply = postCommentForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomComment) + val postedComment = postedReply.body.extract[TransactionCommentJSON] + When("the delete request is sent") + val deleteReply = deleteCommentForOneTransaction(bankId, bankAccount.id, view, transaction.id, postedComment.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + } + + scenario("we will not delete a comment for one random transaction due to a missing token", API1_2, DeleteComment) { + Given("We will not use an access token and will set a comment first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomComment = PostTransactionCommentJSON(randomString(20)) + val postedReply = postCommentForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomComment) + val postedComment = postedReply.body.extract[TransactionCommentJSON] + When("the delete request is sent") + val deleteReply = deleteCommentForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, postedComment.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a comment for one random transaction because the user does not have enough privileges", API1_2, DeleteComment) { + Given("We will use an access token and will set a comment first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomComment = PostTransactionCommentJSON(randomString(20)) + val postedReply = postCommentForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomComment) + val postedComment = postedReply.body.extract[TransactionCommentJSON] + When("the delete request is sent") + val deleteReply = deleteCommentForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, postedComment.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a comment for one random transaction because the user did not post the comment", API1_2, DeleteComment) { + Given("We will use an access token and will set a comment first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "public" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomComment = PostTransactionCommentJSON(randomString(20)) + val postedReply = postCommentForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomComment) + val postedComment = postedReply.body.extract[TransactionCommentJSON] + When("the delete request is sent") + val deleteReply = deleteCommentForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, postedComment.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a comment for one random transaction because the comment does not exist", API1_2, DeleteComment) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the delete request is sent") + val deleteReply = deleteCommentForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a comment for one random transaction because the transaction does not exist", API1_2, DeleteComment) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomComment = PostTransactionCommentJSON(randomString(20)) + val postedReply = postCommentForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomComment) + val postedComment = postedReply.body.extract[TransactionCommentJSON] + When("the delete request is sent") + val deleteReply = deleteCommentForOneTransaction(bankId, bankAccount.id, view, randomString(5), postedComment.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a comment for one random transaction because the view does not exist", API1_2, DeleteComment) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomComment = PostTransactionCommentJSON(randomString(20)) + val postedReply = postCommentForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomComment) + val postedComment = postedReply.body.extract[TransactionCommentJSON] + When("the delete request is sent") + val deleteReply = deleteCommentForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id, postedComment.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We get the tags of one random transaction"){ + scenario("we will get the tags of one random transaction", API1_2, GetTags) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTagsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 200 code") + reply.code should equal (200) + reply.body.extract[TransactionTagsJSON] + } + + scenario("we will not get the tags of one random transaction due to a missing token", API1_2, GetTags) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTagsForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the tags of one random transaction because the user does not have enough privileges", API1_2, GetTags) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTagsForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the tags of one random transaction because the view does not exist", API1_2, GetTags) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTagsForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the tags of one random transaction because the transaction does not exist", API1_2, GetTags) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getTagsForOneTransaction(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We post a tag for one random transaction"){ + scenario("we will post a tag for one random transaction", API1_2, PostTag) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val randomTag = PostTransactionTagJSON(randomString(5)) + val postReply = postTagForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomTag) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[TransactionTagJSON] + And("the tag should be added") + val getReply = getTagsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theTagsAfterThePost = getReply.body.extract[TransactionTagsJSON].tags + val theTag = theTagsAfterThePost.find(_.value == randomTag.value) + theTag.nonEmpty should equal (true) + theTag.get.user should not equal (null) + } + + scenario("we will not post a tag for one random transaction due to a missing token", API1_2, PostTag) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomTag = PostTransactionTagJSON(randomString(5)) + When("the request is sent") + val postReply = postTagForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, randomTag) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the tag should not be added") + val getReply = getTagsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theTagsAfterThePost = getReply.body.extract[TransactionTagsJSON].tags + val notFound = theTagsAfterThePost.find(_.value == randomTag.value) match { + case None => true + case Some(_) => false + } + notFound should equal (true) + } + + scenario("we will not post a tag for one random transaction because the user does not have enough privileges", API1_2, PostTag) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomTag = PostTransactionTagJSON(randomString(5)) + When("the request is sent") + val postReply = postTagForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, randomTag) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the tag should not be added") + val getReply = getTagsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theTagsAfterThePost = getReply.body.extract[TransactionTagsJSON].tags + val notFound = theTagsAfterThePost.find(_.value == randomTag.value) match { + case None => true + case Some(_) => false + } + notFound should equal (true) + } + + scenario("we will not post a tag for one random transaction because the view does not exist", API1_2, PostTag) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomTag = PostTransactionTagJSON(randomString(5)) + When("the request is sent") + val postReply = postTagForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id, randomTag) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the tag should not be added") + val getReply = getTagsForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theTagsAfterThePost = getReply.body.extract[TransactionTagsJSON].tags + val notFound = theTagsAfterThePost.find(_.value == randomTag.value) match { + case None => true + case Some(_) => false + } + notFound should equal (true) + } + + scenario("we will not post a tag for one random transaction because the transaction does not exist", API1_2, PostTag) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomTag = PostTransactionTagJSON(randomString(5)) + When("the request is sent") + val postReply = postTagForOneTransaction(bankId, bankAccount.id, view, randomString(5), randomTag) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete a tag for one random transaction"){ + scenario("we will delete a tag for one random transaction", API1_2, DeleteTag) { + Given("We will use an access token and will set a tag first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomTag = PostTransactionTagJSON(randomString(5)) + val postedReply = postTagForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomTag) + val postedTag = postedReply.body.extract[TransactionTagJSON] + When("the delete request is sent") + val deleteReply = deleteTagForOneTransaction(bankId, bankAccount.id, view, transaction.id, postedTag.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + } + + scenario("we will not delete a tag for one random transaction due to a missing token", API1_2, DeleteTag) { + Given("We will not use an access token and will set a tag first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomTag = PostTransactionTagJSON(randomString(5)) + val postedReply = postTagForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomTag) + val postedTag = postedReply.body.extract[TransactionTagJSON] + When("the delete request is sent") + val deleteReply = deleteTagForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, postedTag.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a tag for one random transaction because the user does not have enough privileges", API1_2, DeleteTag) { + Given("We will use an access token and will set a tag first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomTag = PostTransactionTagJSON(randomString(5)) + val postedReply = postTagForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomTag) + val postedTag = postedReply.body.extract[TransactionTagJSON] + When("the delete request is sent") + val deleteReply = deleteTagForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, postedTag.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a tag for one random transaction because the user did not post the tag", API1_2, DeleteTag) { + Given("We will use an access token and will set a tag first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "public" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomTag = PostTransactionTagJSON(randomString(5)) + val postedReply = postTagForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomTag) + val postedTag = postedReply.body.extract[TransactionTagJSON] + When("the delete request is sent") + val deleteReply = deleteTagForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, postedTag.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a tag for one random transaction because the tag does not exist", API1_2, DeleteTag) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the delete request is sent") + val deleteReply = deleteTagForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a tag for one random transaction because the transaction does not exist", API1_2, DeleteTag) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomTag = PostTransactionTagJSON(randomString(5)) + val postedReply = postTagForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomTag) + val postedTag = postedReply.body.extract[TransactionTagJSON] + When("the delete request is sent") + val deleteReply = deleteTagForOneTransaction(bankId, bankAccount.id, view, randomString(5), postedTag.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete a tag for one random transaction because the view does not exist", API1_2, DeleteTag) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomTag = PostTransactionTagJSON(randomString(5)) + val postedReply = postTagForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomTag) + val postedTag = postedReply.body.extract[TransactionTagJSON] + When("the delete request is sent") + val deleteReply = deleteTagForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id, postedTag.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We get the images of one random transaction"){ + scenario("we will get the images of one random transaction", API1_2, GetImages) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getImagesForOneTransaction(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 200 code") + reply.code should equal (200) + reply.body.extract[TransactionImagesJSON] + } + + scenario("we will not get the images of one random transaction due to a missing token", API1_2, GetImages) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getImagesForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the images of one random transaction because the user does not have enough privileges", API1_2, GetImages) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getImagesForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the images of one random transaction because the view does not exist", API1_2, GetImages) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getImagesForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the images of one random transaction because the transaction does not exist", API1_2, GetImages) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getImagesForOneTransaction(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We post an image for one random transaction"){ + scenario("we will post an image for one random transaction", API1_2, PostImage) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + val postReply = postImageForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomImage) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[TransactionImageJSON] + And("the image should be added") + val getReply = getImagesForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theImagesAfterThePost = getReply.body.extract[TransactionImagesJSON].images + val theImage = theImagesAfterThePost.find(_.URL == randomImage.URL) + theImage.nonEmpty should equal (true) + theImage.get.user should not equal (null) + } + + scenario("we will not post an image for one random transaction due to a missing token", API1_2, PostImage) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + When("the request is sent") + val postReply = postImageForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, randomImage) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the image should not be added") + val getReply = getImagesForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theImagesAfterThePost = getReply.body.extract[TransactionImagesJSON].images + val notFound = theImagesAfterThePost.find(_.URL == randomImage.URL) match { + case None => true + case Some(_) => false + } + notFound should equal (true) + } + + scenario("we will not post an image for one random transaction because the user does not have enough privileges", API1_2, PostImage) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + When("the request is sent") + val postReply = postImageForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, randomImage) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the image should not be added") + val getReply = getImagesForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theImagesAfterThePost = getReply.body.extract[TransactionImagesJSON].images + val notFound = theImagesAfterThePost.find(_.URL == randomImage.URL) match { + case None => true + case Some(_) => false + } + notFound should equal (true) + } + + scenario("we will not post an image for one random transaction because the view does not exist", API1_2, PostImage) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + When("the request is sent") + val postReply = postImageForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id, randomImage) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + And("the image should not be added") + val getReply = getImagesForOneTransaction(bankId, bankAccount.id, view, transaction.id) + val theImagesAfterThePost = getReply.body.extract[TransactionImagesJSON].images + val notFound = theImagesAfterThePost.find(_.URL == randomImage.URL) match { + case None => true + case Some(_) => false + } + notFound should equal (true) + } + + scenario("we will not post an image for one random transaction because the transaction does not exist", API1_2, PostImage) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + When("the request is sent") + val postReply = postImageForOneTransaction(bankId, bankAccount.id, view, randomString(5), randomImage) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete an image for one random transaction"){ + scenario("we will delete an image for one random transaction", API1_2, DeleteImage) { + Given("We will use an access token and will set an image first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + val postedReply = postImageForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomImage) + val postedImage = postedReply.body.extract[TransactionImageJSON] + When("the delete request is sent") + val deleteReply = deleteImageForOneTransaction(bankId, bankAccount.id, view, transaction.id, postedImage.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + } + + scenario("we will not delete an image for one random transaction due to a missing token", API1_2, DeleteImage) { + Given("We will not use an access token and will set an image first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + val postedReply = postImageForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomImage) + val postedImage = postedReply.body.extract[TransactionImageJSON] + When("the delete request is sent") + val deleteReply = deleteImageForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, postedImage.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete an image for one random transaction because the user does not have enough privileges", API1_2, DeleteImage) { + Given("We will use an access token and will set an image first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + val postedReply = postImageForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomImage) + val postedImage = postedReply.body.extract[TransactionImageJSON] + When("the delete request is sent") + val deleteReply = deleteImageForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, postedImage.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete an image for one random transaction because the user did not post the image", API1_2, DeleteImage) { + Given("We will use an access token and will set an image first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "public" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + val postedReply = postImageForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomImage) + val postedImage = postedReply.body.extract[TransactionImageJSON] + When("the delete request is sent") + val deleteReply = deleteImageForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, postedImage.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete an image for one random transaction because the image does not exist", API1_2, DeleteImage) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the delete request is sent") + val deleteReply = deleteImageForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete an image for one random transaction because the transaction does not exist", API1_2, DeleteImage) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + val postedReply = postImageForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomImage) + val postedImage = postedReply.body.extract[TransactionImageJSON] + When("the delete request is sent") + val deleteReply = deleteImageForOneTransaction(bankId, bankAccount.id, view, randomString(5), postedImage.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete an image for one random transaction because the view does not exist", API1_2, DeleteImage) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomImage = PostTransactionImageJSON(randomString(5),"http://www.mysuperimage.com") + val postedReply = postImageForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomImage) + val postedImage = postedReply.body.extract[TransactionImageJSON] + When("the delete request is sent") + val deleteReply = deleteImageForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id, postedImage.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We get the where of one random transaction"){ + scenario("we will get the where of one random transaction", API1_2, GetWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalinkAllowingViewPrivilige + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + When("the request is sent") + val reply = getWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 200 code") + reply.code should equal (200) + } + + scenario("we will not get the where of one random transaction due to a missing token", API1_2, GetWhere) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + When("the request is sent") + val reply = getWhereForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the where of one random transaction because the user does not have enough privileges", API1_2, GetWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + When("the request is sent") + val reply = getWhereForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the where of one random transaction because the view does not exist", API1_2, GetWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + When("the request is sent") + val reply = getWhereForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get the where of one random transaction because the transaction does not exist", API1_2, GetWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getWhereForOneTransaction(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We post the where for one random transaction"){ + scenario("we will post the where for one random transaction", API1_2, PostWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + Then("we should get a 201 code") + postReply.code should equal (201) + postReply.body.extract[SuccessMessage] + And("the where should be posted") + val location = getWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id).body.extract[TransactionWhereJSON] + randomLoc.latitude should equal (location.where.latitude) + randomLoc.longitude should equal (location.where.longitude) + location.where.user should not equal (null) + } + + scenario("we will not post the where for one random transaction because the coordinates don't exist", API1_2, PostWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + var randomLoc = JSONFactory.createLocationPlainJSON(400,200) + When("the request is sent") + val postReply = postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the where for a random transaction due to a missing token", API1_2, PostWhere) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postWhereForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the where for a random transaction because the user does not have enough privileges", API1_2, PostWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postWhereForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the where for a random transaction because the view does not exist", API1_2, PostWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val postReply = postWhereForOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id, randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not post the where for a random transaction because the transaction does not exist", API1_2, PostWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomLoc = randomLocation + When("the request is sent") + val postReply = postWhereForOneTransaction(bankId, bankAccount.id, view, randomString(5), randomLoc) + Then("we should get a 400 code") + postReply.code should equal (400) + And("we should get an error message") + postReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We update the where for one random transaction"){ + scenario("we will update the where for one random transaction", API1_2, PutWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val randomLoc = randomLocation + val putReply = updateWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + Then("we should get a 200 code") + putReply.code should equal (200) + putReply.body.extract[SuccessMessage] + And("the where should be changed") + val location = getWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id).body.extract[TransactionWhereJSON] + randomLoc.latitude should equal (location.where.latitude) + randomLoc.longitude should equal (location.where.longitude) + } + + scenario("we will not update the where for one random transaction because the coordinates don't exist", API1_2, PutWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + var randomLoc = JSONFactory.createLocationPlainJSON(400,200) + When("the request is sent") + val putReply = updateWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the where for a random transaction due to a missing token", API1_2, PutWhere) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + var randomLoc = randomLocation + When("the request is sent") + val putReply = updateWhereForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id, randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the where for a random transaction because the user does not have enough privileges", API1_2, PutWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + When("the request is sent") + val putReply = updateWhereForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id, randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not update the where for a random transaction because the transaction does not exist", API1_2, PutWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomLoc = randomLocation + When("the request is sent") + val putReply = updateWhereForOneTransaction(bankId, bankAccount.id, view, randomString(5), randomLoc) + Then("we should get a 400 code") + putReply.code should equal (400) + And("we should get an error message") + putReply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } + + feature("We delete the where for one random transaction"){ + scenario("we will delete the where for one random transaction", API1_2, DeleteWhere) { + Given("We will use an access token and will set a where tag first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + When("the delete request is sent") + val deleteReply = deleteWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 204 code") + deleteReply.code should equal (204) + And("the where should be null") + // TODO: (3 scenarios) + // val locationAfterDelete = getWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id).body.extract[TransactionWhereJSON] + // locationAfterDelete.where should equal (null) + } + + scenario("we will not delete the where for a random transaction due to a missing token", API1_2, DeleteWhere) { + Given("We will not use an access token and will set a where tag first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + When("the delete request is sent") + val deleteReply = deleteWhereForOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + // And("the where should not be null") + } + + scenario("we will not delete the where for a random transaction because the user does not have enough privileges", API1_2, DeleteWhere) { + Given("We will use an access token and will set a where tag first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + When("the delete request is sent") + val deleteReply = deleteWhereForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + // And("the where should not be null") + } + + scenario("we will not delete the where for one random transaction because the user did not post the geo tag", API1_2, DeleteWhere) { + Given("We will use an access token and will set a where tag first") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = "public" + val transaction = randomTransaction(bankId, bankAccount.id, view) + val randomLoc = randomLocation + postWhereForOneTransaction(bankId, bankAccount.id, view, transaction.id, randomLoc) + When("the delete request is sent") + val deleteReply = deleteWhereForOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + + scenario("we will not delete the where for a random transaction because the transaction does not exist", API1_2, DeleteWhere) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val randomLoc = randomLocation + When("the delete request is sent") + val deleteReply = deleteWhereForOneTransaction(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + deleteReply.code should equal (400) + } + } + + feature("We get the other bank account of a transaction "){ + scenario("we will get the other bank account of a random transaction", API1_2, GetTransactionAccount) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTheOtherBankAccountOfOneTransaction(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 200 code") + reply.code should equal (200) + val accountJson = reply.body.extract[OtherAccountJSON] + And("some fields should not be empty") + accountJson.id.nonEmpty should equal (true) + } + + scenario("we will not get the other bank account of a random transaction due to a missing token", API1_2, GetTransactionAccount) { + Given("We will not use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTheOtherBankAccountOfOneTransactionWithoutToken(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get get the other bank account of a random transaction because the user does not have enough privileges", API1_2, GetTransactionAccount) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTheOtherBankAccountOfOneTransactionWithWrongUser(bankId, bankAccount.id, view, transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get get the other bank account of a random transaction because the view does not exist", API1_2, GetTransactionAccount) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + val transaction = randomTransaction(bankId, bankAccount.id, view) + When("the request is sent") + val reply = getTheOtherBankAccountOfOneTransaction(bankId, bankAccount.id, randomString(5), transaction.id) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + + scenario("we will not get get the other bank account of a random transaction because the transaction does not exist", API1_2, GetTransactionAccount) { + Given("We will use an access token") + val bankId = randomBank + val bankAccount : AccountJSON = randomPrivateAccount(bankId) + val view = randomViewPermalink + When("the request is sent") + val reply = getTheOtherBankAccount(bankId, bankAccount.id, view, randomString(5)) + Then("we should get a 400 code") + reply.code should equal (400) + And("we should get an error message") + reply.body.extract[ErrorMessage].error.nonEmpty should equal (true) + } + } +} \ No newline at end of file diff --git a/MavLift/src/test/scala/code/api/ServerSetup.scala b/MavLift/src/test/scala/code/api/ServerSetup.scala new file mode 100644 index 000000000..8f52fd2f5 --- /dev/null +++ b/MavLift/src/test/scala/code/api/ServerSetup.scala @@ -0,0 +1,195 @@ +package code.api.test + +import org.scalatest._ +import dispatch._ +import dispatch.liftjson.Js._ +import net.liftweb.json.NoTypeHints +import net.liftweb.json.JsonAST.{JValue, JObject} +import _root_.net.liftweb.json.Serialization.write +import net.liftweb.common._ +import org.mortbay.jetty.Connector +import org.mortbay.jetty.Server +import org.mortbay.jetty.nio.SelectChannelConnector +import org.mortbay.jetty.webapp.WebAppContext +import net.liftweb.json.Serialization +import org.junit.runner.RunWith +import net.liftweb.mongodb._ +import net.liftweb.util.Props +import code.model.dataAccess.{OBPUser, Privilege, HostedAccount, Account, HostedBank} +import java.util.Date +import _root_.net.liftweb.util._ +import Helpers._ +import org.bson.types.ObjectId +import scala.util.Random._ +case class APIResponse(code: Int, body: JValue) + +trait ServerSetup extends FeatureSpec + with BeforeAndAfterAll with GivenWhenThen + with ShouldMatchers with Loggable{ + + val server = ServerSetup + + implicit val formats = Serialization.formats(NoTypeHints) + + val h = new Http with thread.Safety + val baseRequest = (:/(server.host, Integer.valueOf(server.port))) + + + override def beforeAll() = { + implicit val dateFormats = net.liftweb.json.DefaultFormats + //create fake data for the tests + + //fake banks + val banks = for{i <- 0 until 3} yield { + HostedBank.createRecord. + name(randomString(5)). + alias(randomString(5)). + permalink(randomString(5)). + save + } + + //fake bank accounts + val accounts = banks.flatMap(bank => { + for { i <- 0 until 2 } yield { + Account.createRecord. + balance(0). + anonAccess(true). + holder(randomString(4)). + number(randomString(4)). + kind(randomString(4)). + name(randomString(4)). + permalink(randomString(4)). + bankID(new ObjectId(bank.id.get.toString)). + label(randomString(4)). + currency(randomString(4)). + save + } + }) + + val hostedAccounts = accounts.map(account => { + HostedAccount.create.accountID(account.id.get.toString).saveMe + }) + + case class OBPTransactionWrapper( + obp_transaction: OBPTransaction) + + case class OBPTransaction( + this_account: OBPAccount, + other_account: OBPAccount, + details: OBPDetails) + + case class OBPAccount( + holder: String, + number: String, + kind: String, + bank: OBPBank) + + case class OBPBank( + IBAN: String, + national_identifier: String, + name: String) + + case class OBPDetails( + type_en: String, + type_de: String, + posted: Date, + completed: Date, + new_balance: OBPAmount, + value: OBPAmount, + label: String, + other_data: String) + + case class OBPAmount( + currency: String, + amount: String) { + override def toString = "OBPAmount(" + currency + ",***)" + } + + //fake transactions + val postTransaction = baseRequest / "api"/ "transactions" + accounts.foreach(account => { + val transactions = + for{i <- 0 until 10} yield{ + val thisAccountBank = OBPBank("", "", account.bankName) + val thisAccount = OBPAccount(account.holder.get, account.number.get, account.kind.get, thisAccountBank) + val otherAccountBank = OBPBank("", "", randomString(5)) + val otherAccount = OBPAccount(randomString(5), randomString(5), randomString(5), otherAccountBank) + val balance = OBPAmount(randomString(3), nextInt(100).toString) + val value = OBPAmount(randomString(3), nextInt(100).toString) + val details = OBPDetails(randomString(5), randomString(5), now, now, balance, value, randomString(10), "") + val obpTransaction = OBPTransaction(thisAccount, otherAccount, details) + OBPTransactionWrapper(obpTransaction) + } + makePostRequest(postTransaction, write(transactions)) + }) + specificSetup() + } + + //this method is to run a specific behavior before running each test class + def specificSetup() = { + } + + override def afterAll() = { + //drop the Database after the tests + MongoDB.getDb(DefaultMongoIdentifier).map(_.dropDatabase()) + } + /** + this method do a post request given a URL, a JSON and an optional Headers Map + */ + def makePostRequest(req: Request, json: String = "", headers : Map[String,String] = Map()) : h.HttpPackage[APIResponse] = { + val jsonReq = req << (json, "application/json") <:< headers + val jsonHandler = jsonReq ># {json => json} + h x jsonHandler{ + case (code, _, _, json) => APIResponse(code, json()) + } + } + + def makePutRequest(req: Request, json: String, headers : Map[String,String] = Map(("Content-type","application/json"))) : h.HttpPackage[APIResponse] = { + val jsonReq = req <<< json <:< headers + val jsonHandler = jsonReq ># {json => json} + h x jsonHandler{ + case (code, _, _, json) => APIResponse(code, json()) + } + } + + /** + * this method do a post request given a URL + */ + def makeGetRequest(req: Request, headers : Map[String,String] = Map()) : h.HttpPackage[APIResponse] = { + val jsonReq = req <:< headers + val jsonHandler = jsonReq ># {json => json} + h x jsonHandler{ + case (code, _, _, json) => APIResponse(code, json()) + } + } + + /** + * this method do a delete request given a URL + */ + def makeDeleteRequest(req: Request, headers : Map[String,String] = Map()) : h.HttpPackage[APIResponse] = { + val jsonReq = (req <:< headers).DELETE + val jsonHandler = jsonReq.>| + h x jsonHandler{ + case (code, _, _, _) => APIResponse(code, null) + } + } +} + +object ServerSetup { + + val host = "localhost" + val port = 8000 + val server = new Server + val scc = new SelectChannelConnector + scc.setPort(port) + server.setConnectors(Array(scc)) + + val context = new WebAppContext() + context.setServer(server) + context.setContextPath("/") + context.setWar("src/main/webapp") + + server.addHandler(context) + + server.start() +} \ No newline at end of file diff --git a/README b/README deleted file mode 100644 index 904d7fb9f..000000000 --- a/README +++ /dev/null @@ -1,53 +0,0 @@ -# README - -Welcome to the Open Bank Project API - -## ABOUT - -The API aims to provide a read only access to bank transaction in a simple and consistent structure by abstracting away the peculiarities of each banking system. - -The API aims also to facilitate the data sharing with users, with several level of details in a secure way and to enhance the raw transactions with some metadata : comments, tags, pictures etc. - -The API provides also OAuth 1.0 authentication. - -## Document - -Please refer to the [wiki](https://github.com/OpenBankProject/OBP-API/wiki) to see the API specification. - - -## STATUS - -For the moment some ready only API calls are implemented, by in the future there will be write API calls to post metadata like : comments, pictures, etc. - - -## LICENSE - -This project is dual licensed under the AGPL V3 (see NOTICE) and a commercial license from TESOBE -Some files (OAuth related) are licensed under the Apache 2 license. - -## SETUP - -We recommend to use the Vagrant and Puppet scripts available [here](https://github.com/OpenBankProject/OBP-VM) to create a Virtual Box VM running the Open Bank Project API. - -Other wise the project is using sbt or Maven 2 as a build tool. - ----- - -To compile and run jetty, cd into the "MavLift" directory and run: - -$ sbt -... -> compile -> ~;container:start; container:reload / - -(Note that you first have to start sbt and then on its console start jetty with the container:start task, otherwise it will exit immediately. More here: https://github.com/siasia/xsbt-web-plugin/wiki) - -In OS X, sbt can be installed with $ sudo port install sbt - ----- - -Alternatively, maven can also be used: - -mvn jetty:run - -You need to install MongoDB and create an empty database called "OBP006". \ No newline at end of file