updated the API project files

This commit is contained in:
Ayoub BENALI 2013-06-14 10:31:36 +02:00
parent b4b5655f0b
commit cf1e83b5a3
63 changed files with 16030 additions and 3823 deletions

7
.gitignore vendored
View File

@ -8,6 +8,7 @@
.settings
.classpath
.project
MavLift/.cache
MavLift/src/main/resources/rebel.xml
MavLift/src/main/resources/props
.cache
target
MavLift/src/main/resources/
MavLift/src/test/resources/

3
MavLift/.gitignore vendored
View File

@ -1,2 +1 @@
target
src/main/resources/

View File

@ -1,295 +1,347 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tesobe</groupId>
<artifactId>opan_bank</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>Opan Bank</name>
<inceptionYear>2011</inceptionYear>
<properties>
<scala.version>2.9.1</scala.version>
<!-- Common plugin settings -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>${project.build.sourceEncoding}</project.reporting.outputEncoding>
<!-- vscaladoc settings -->
<maven.scaladoc.vscaladocVersion>1.2-m1</maven.scaladoc.vscaladocVersion>
<vscaladoc.links.liftweb.pathsufix>scaladocs/</vscaladoc.links.liftweb.pathsufix>
<vscaladoc.links.liftweb.baseurl>http://scala-tools.org/mvnsites/liftweb</vscaladoc.links.liftweb.baseurl>
</properties>
<repositories>
<repository>
<id>sonatype.releases</id>
<name>sonatype Dependencies Repository for Releases</name>
<url>https://oss.sonatype.org/content/groups/scala-tools</url>
</repository>
<repository>
<id>sonatype.snapshots</id>
<name>sonatype Dependencies Repository for Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
<repository>
<id>scala-tools.releases</id>
<name>Scala-Tools Dependencies Repository for Releases</name>
<url>http://scala-tools.org/repo-releases</url>
</repository>
<repository>
<id>scala-tools.snapshots</id>
<name>Scala-Tools Dependencies Repository for Snapshots</name>
<url>http://scala-tools.org/repo-snapshots</url>
</repository>
</repositories>
<modelVersion>4.0.0</modelVersion>
<groupId>com.tesobe</groupId>
<artifactId>OBP-API</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>Open Bank Project API</name>
<inceptionYear>2011</inceptionYear>
<properties>
<scala.version>2.9.1</scala.version>
<lift.version>2.4</lift.version>
<!-- Common plugin settings -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>${project.build.sourceEncoding}</project.reporting.outputEncoding>
<!-- vscaladoc settings -->
<maven.scaladoc.vscaladocVersion>1.2-m1</maven.scaladoc.vscaladocVersion>
<vscaladoc.links.liftweb.pathsufix>scaladocs/</vscaladoc.links.liftweb.pathsufix>
<vscaladoc.links.liftweb.baseurl>http://scala-tools.org/mvnsites/liftweb</vscaladoc.links.liftweb.baseurl>
</properties>
<pluginRepositories>
<repositories>
<repository>
<id>scala-tools.releases</id>
<name>Scala-Tools Dependencies Repository for Releases</name>
<url>https://oss.sonatype.org/content/groups/scala-tools/</url>
</repository>
<repository>
<id>java.net.maven2</id>
<name>java.net Maven2 Repository</name>
<url>http://download.java.net/maven/2/</url>
</repository>
<repository>
<id>scala-tools.snapshots</id>
<name>Scala-Tools Dependencies Repository for Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>sonatype.releases</id>
<name>sonatype Dependencies Repository for Releases</name>
<url>https://oss.sonatype.org/content/groups/scala-tools</url>
<id>org.sonatype.oss.groups.public</id>
<name>Sonatype Public</name>
<url>https://oss.sonatype.org/content/groups/public</url>
</pluginRepository>
<pluginRepository>
<id>scala-tools.releases</id>
<name>Scala-Tools Plugins Repository for Releases</name>
<url>http://scala-tools.org/repo-releases</url>
</pluginRepository>
</pluginRepositories>
</pluginRepositories>
<dependencies>
<dependencies>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-mapper_${scala.version}</artifactId>
<version>${lift.version}</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-widgets_${scala.version}</artifactId>
<version>${lift.version}</version>
</dependency>
<dependency>
<groupId>net.databinder</groupId>
<artifactId>dispatch-http_${scala.version}</artifactId>
<version>0.8.6</version>
</dependency>
<dependency>
<groupId>net.databinder</groupId>
<artifactId>dispatch-oauth_${scala.version}</artifactId>
<version>0.8.6</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-mapper_2.9.1</artifactId>
<version>2.4-M4</version>
<groupId>net.databinder</groupId>
<artifactId>dispatch-lift-json_${scala.version}</artifactId>
<version>0.8.5</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-widgets_2.9.1</artifactId>
<version>2.4-M4</version>
</dependency>
<dependency>
<groupId>net.databinder</groupId>
<artifactId>dispatch-http_${scala.version}</artifactId>
<version>0.8.6</version>
</dependency>
<dependency>
<groupId>net.databinder</groupId>
<artifactId>dispatch-oauth_${scala.version}</artifactId>
<version>0.8.6</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.26</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.4-701.jdbc4</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.2.138</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scala-tools.testing</groupId>
<artifactId>specs_2.9.1</artifactId>
<version>1.6.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk16</artifactId>
<version>1.46</version>
</dependency>
<!-- for LiftConsole -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.26</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.4-701.jdbc4</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.2.138</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_${scala.version}</artifactId>
<version>2.0.M5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scala-tools.testing</groupId>
<artifactId>specs_${scala.version}</artifactId>
<version>1.6.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk16</artifactId>
<version>1.46</version>
</dependency>
<!-- for LiftConsole -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-compiler</artifactId>
<version>${scala.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-compiler</artifactId>
<version>${scala.version}</version>
<scope>test</scope>
<artifactId>scala-library</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-mongodb_2.9.1</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-mongodb-record_2.9.1</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>casbah_${scala.version}</artifactId>
<version>2.3.0</version>
<scope>compile</scope>
<type>pom</type>
</dependency>
</dependencies>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-mongodb_${scala.version}</artifactId>
<version>${lift.version}</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-mongodb-record_${scala.version}</artifactId>
<version>${lift.version}</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>casbah_${scala.version}</artifactId>
<version>2.3.0</version>
<scope>compile</scope>
<type>pom</type>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>2.9</version>
<configuration>
<charset>${project.build.sourceEncoding}</charset>
<jvmArgs>
<jvmArg>-Xmx1024m</jvmArg>
<jvmArg>-DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties</jvmArg>
</jvmArgs>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4.2</version>
<executions>
<execution>
<id>default-copy-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<overwrite>true</overwrite>
<outputDirectory>${project.build.directory}</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src</directory>
<includes>
<include>packageLinkDefs.properties</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.25</version>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>5</scanIntervalSeconds>
</configuration>
</plugin>
<plugin>
<groupId>net.sf.alchim</groupId>
<artifactId>yuicompressor-maven-plugin</artifactId>
<version>0.7.1</version>
<executions>
<execution>
<goals>
<goal>compress</goal>
</goals>
</execution>
</executions>
<configuration>
<nosuffix>true</nosuffix>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-idea-plugin</artifactId>
<version>2.2</version>
<configuration>
<downloadSources>true</downloadSources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.7</version>
<configuration>
<downloadSources>true</downloadSources>
<additionalProjectnatures>
<projectnature>ch.epfl.lamp.sdt.core.scalanature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>ch.epfl.lamp.sdt.core.scalabuilder</buildcommand>
</additionalBuildcommands>
<classpathContainers>
<classpathContainer>ch.epfl.lamp.sdt.launching.SCALA_CONTAINER</classpathContainer>
<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
</classpathContainers>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.1.0</version>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>src/main/resources/git.properties</generateGitPropertiesFilename>
<failOnNoGitDirectory>false</failOnNoGitDirectory>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>2.14.3</version>
<configuration>
<charset>${project.build.sourceEncoding}</charset>
<jvmArgs>
<jvmArg>-Xmx1024m</jvmArg>
<jvmArg>-DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties</jvmArg>
</jvmArgs>
</configuration>
</plugin>
</plugins>
</reporting>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<!-- enable the scalatest plugin -->
<groupId>org.scalatest</groupId>
<artifactId>maven-scalatest-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<configuration>
<reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
<junitxml>.</junitxml>
<filereports>WDF TestSuite.txt</filereports>
<argLine>-Drun.mode=test</argLine>
</configuration>
<executions>
<execution>
<id>test</id>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<!-- add src/main/java to source dirs -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>2.15.2</version>
<configuration>
<charset>${project.build.sourceEncoding}</charset>
<jvmArgs>
<jvmArg>-Xmx1024m</jvmArg>
<jvmArg>-DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties</jvmArg>
</jvmArgs>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>default-copy-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<overwrite>true</overwrite>
<outputDirectory>${project.build.directory}</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src</directory>
<includes>
<include>packageLinkDefs.properties</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.25</version>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>5</scanIntervalSeconds>
</configuration>
</plugin>
<plugin>
<groupId>net.sf.alchim</groupId>
<artifactId>yuicompressor-maven-plugin</artifactId>
<version>0.7.1</version>
<executions>
<execution>
<goals>
<goal>compress</goal>
</goals>
</execution>
</executions>
<configuration>
<nosuffix>true</nosuffix>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-idea-plugin</artifactId>
<version>2.2</version>
<configuration>
<downloadSources>true</downloadSources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.7</version>
<configuration>
<downloadSources>true</downloadSources>
<additionalProjectnatures>
<projectnature>ch.epfl.lamp.sdt.core.scalanature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>ch.epfl.lamp.sdt.core.scalabuilder</buildcommand>
</additionalBuildcommands>
<classpathContainers>
<classpathContainer>ch.epfl.lamp.sdt.launching.SCALA_CONTAINER</classpathContainer>
<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
</classpathContainers>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.1.0</version>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>src/main/resources/git.properties</generateGitPropertiesFilename>
<failOnNoGitDirectory>false</failOnNoGitDirectory>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>2.15.2</version>
<configuration>
<charset>${project.build.sourceEncoding}</charset>
<jvmArgs>
<jvmArg>-Xmx1024m</jvmArg>
<jvmArg>-DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties</jvmArg>
</jvmArgs>
</configuration>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -1,2 +1,2 @@
#Project properties
sbt.version=0.11.2
sbt.version=0.11.3

View File

@ -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"),

View File

@ -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/"

View File

@ -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 + "'");
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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")
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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))
}
})
}

File diff suppressed because it is too large Load Diff

View File

@ -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)
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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)
}
})
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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)
}
})
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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()
}
}
}
}

View File

@ -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
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <http://www.gnu.org/licenses/>.
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
)

View File

@ -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 <http://www.gnu.org/licenses/>.
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
)

View File

@ -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 <http://www.gnu.org/licenses/>.
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)
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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]{}

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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"
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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(<lift:surround with="default" at="content">
<lift:bind /></lift:surround>)
// 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
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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)
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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")
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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]

View File

@ -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 <http://www.gnu.org/licenses/>.
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(<lift:surround with="default" at="content">
<lift:bind /></lift:surround>)
// 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(<lift:surround with="default" at="content">
<lift:bind /></lift:surround>)
// 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 = <span>User email</span>
override def displayHtml = <span>User email</span>
override def asHtml = {
val email = (for {
u <- OBPUser.find(user.get)
@ -236,35 +199,35 @@ class Privilege extends LongKeyedMapper[Privilege] with CreatedUpdated{
<span>{email}</span>
}
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 = <span>Account</span>
override def displayHtml = <span>Account</span>
override def asHtml = {
<span>{
HostedAccount.find(account.get) match {
case Full(account) => account.bank + " - " + account.name
case _ => "account not found"
}
}</span>
}</span>
}
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]{}

View File

@ -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]

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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)
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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)
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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_
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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_
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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"
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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]
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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)
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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" -> "")
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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)
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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! ")
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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 "" => {
<div onclick={ setAndSwap(editName, editMarkup, dispName).toJsCmd + " return false;" }><a href="#" class={ addClass }>{
" " ++ displayText
}</a></div>
}
case _ => {
<div>
<a href="#" class={ editClass } onclick={ setAndSwap(editName, editMarkup, dispName).toJsCmd + " return false;" }/>
<span class="text">{ label }</span>
</div>
}
}
}
def editMarkup: NodeSeq = {
val formData: NodeSeq =
editForm ++ <br />
<input class="submit" style="float:left;" type="image" src="/media/images/submit.png"/> ++
SHtml.hidden(onSubmit, ("float", "left")) ++
<input type="image" src="/media/images/cancel.png" onclick={ swapJsCmd(dispName, editName).toJsCmd + " return false;" }/>
SHtml.ajaxForm(formData,
Noop,
setAndSwap(dispName, displayMarkup, editName))
}
<div>
<div id={ dispName }>
{ displayMarkup }
</div>
<div id={ editName } style="display: none;">
{ editMarkup }
</div>
</div>
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}
}
}
}

View File

@ -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_!)
}
}

View File

@ -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_!)
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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()
}
}
}
}
}

View File

@ -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)
}

View File

@ -0,0 +1,166 @@
<!--
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 <http://www.gnu.org/licenses/>.
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
-->
<div id="main" class="lift:surround?with=default;at=content">
<p>
You can connect to your bank account using this page.
</p>
<br />
<p>
Once an account is connected you can view your transaction data here - and can share the data with other people too.
</p>
<div class="lift:AccountRegistration.renderForm">
<div class="lift:Msg?id=submissionMessage&noticeClass=submission">message</div>
<form action="/connect" method="post" id="submitAccount" >
<TABLE>
<tbody>
<tr>
<td colspan="2" >
Please complete the information below so we can connect to your bank account.
<a href="#registrationDetails"> *</a> </td>
</tr>
<tr>
<td>
<label for="bankList">Bank</label>
</td>
<td>
<span id="bankListCol">
<select>
<option id="01"> post bank</option>
</select>
</span>
<span class="lift:Msg?id=bankError&errorClass=error">error</span>
</td>
</tr>
<tr>
<td>
<label for="accountNumber">Account Number</label>
</td>
<td>
<span id="accountNumberCol">
<input type="text"/>
</span>
<span class="lift:Msg?id=accountNumberError&errorClass=error">error</span>
</td>
</tr>
<tr>
<td>
<label for="accountPIN">PIN code</label>
</td>
<td>
<span id="accountPINCol">
<input type="password" />
</span>
<span class="lift:Msg?id=accountPINError&errorClass=error">error</span>
</td>
</tr>
<tr>
<td>
<label for="accountName">Short name</label>
<img src="/media/images/moreInfo.png" title="It will to determine the account URL and be used instead of the account number" alt="more info" class="moreInfoIcon"/>
</td>
<td>
<span id="accountNameCol">
<input type="text" />
</span>
<span class="lift:Msg?id=accountNameError&errorClass=error">error</span>
</td>
</tr>
<tr>
<td>
<label for="accountLabel">Account description</label>
<img src="/media/images/moreInfo.png" title="It will be shown on the top of each page"
alt="more info" class="moreInfoIcon"/>
</td>
<td>
<span id="accountLabelCol">
<input type="text" />
</span>
<span class="lift:Msg?id=accountLabelError&errorClass=error">error</span>
</td>
</tr>
<tr>
<td>
<label for="accountHolder">Holder Name</label>
<img src="/media/images/moreInfo.png" title="The person who owns the account"
alt="more info" class="moreInfoIcon"/>
</td>
<td>
<span id="accountHolderCol">
<input type="text" />
</span>
<span class="lift:Msg?id=accountHolderError&errorClass=error">error</span>
</td>
</tr>
<tr>
<td>
<label for="accountKind">Account type</label>
<img src="/media/images/moreInfo.png" title="current, checking, saving,etc."
alt="more info" class="moreInfoIcon"/>
</td>
<td>
<span id="accountKindCol">
<input type="text" />
</span>
<span class="lift:Msg?id=accountKindError&errorClass=error">error</span>
</td>
</tr>
<tr>
<td>
Public view
<img src="/media/images/moreInfo.png" title="Do you want to allow a public view on your account ?"
alt="more info" class="moreInfoIcon"/>
</td>
<td>
<span id="publicViewCol"></span>
<span class="lift:Msg?id=publicViewError&errorClass=error">error</span>
</td>
</tr>
<tr>
<td colspan="2" >
<span id="registrationDetails">
(*) : Your PIN code will be securely encrypted. We will not disclose your login data to anyone.
</span>
</td>
</tr>
<tr>
<td>
</td>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</tbody>
</TABLE>
</form>
<div id="loginMsg"></div>
</div>
</div>

View File

@ -0,0 +1,105 @@
<!--
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 <http://www.gnu.org/licenses/>.
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
-->
<div id="main" class="lift:surround?with=default;at=content">
<div class="lift:ConsumerRegistration.registerForm">
<h1 class="register-header">Register to use the Open Bank API with your application:</h1>
<div class="registration">
<div>
<span class="registration-error"></span>
</div>
<form class="register" method="post">
<TABLE>
<tbody>
<tr>
<td colspan="2" >
Please complete the information below so we can get an OAuth key and secret.
</td>
</tr>
<tr>
<td>
<label for="appType">Application Type</label>
</td>
<td>
<select name="app-type" id="appType">
<option class="app-type-option"></option>
</select>
</td>
</tr>
<tr>
<td>
<label for="appName">Application Name</label>
</td>
<td>
<input type="text" name="app-name" id="appName"></input>
</td>
</tr>
<tr>
<td>
<label for="appDev">Developer Email</label>
</td>
<td>
<input type="text" name="app-developer" id="appDev"></input>
</td>
</tr>
<tr>
<td>
<label for="appDesc">App Description</label>
</td>
<td>
<textarea rows="4" name="app-description" id="appDesc"></textarea>
</td>
</tr>
<tr>
<td>
</td>
<td>
<input type="submit" value="Register" />
</td>
</tr>
</tbody>
</TABLE>
</form>
<div class="success">
<h1 class="success-message">Thank you for registering to use the Open Bank API. Here is your developer information. Please save it in a secure location.</h1>
<div class="user-generated">
<div class="info">Application Name: <span class=app-name>ABC</span></div>
<div class="info">Application Type: <span class=app-type>web</span></div>
<div class="info">Description: <span class=app-description>ABCDEF</span></div>
<div class="info">Developer Email: <span class=app-developer>abc@example.com</span></div>
</div>
<div class="server-generated">
<div class="info">Consumer Key: <span class="auth-key">23432432432432</span></div>
<div class="info">Consumer Secret: <span class="secret-key">3334543543543</span></div>
</div>
</div>
</div>
</div>

View File

@ -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 <http://www.gnu.org/licenses/>.
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,5 +30,7 @@ Berlin 13359, Germany
-->
<div id="main" class="lift:surround?with=default;at=content">
Hello !
<p>
Welcome to The Open Bank Project API.
</p>
</div>

View File

@ -1,4 +1,4 @@
/**
<!--
Open Bank Project - Transparency / Social Finance Web Application
Copyright (C) 2011, 2012, TESOBE / Music Pictures Ltd
@ -15,27 +15,26 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Email: contact@tesobe.com
TESOBE / 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
Tim Kleinschmidt: tim@tesobe.com
-->
<div id="main" class="lift:surround?with=default;at=content">
*/
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
}
<link href="media/css/graphs.css" rel="stylesheet">
<h1>Openbank Graphs</h1>
<br>
<script src="media/js/d3.v3.js"></script>
<script src="media/js/graphs.js"></script>
</div>

View File

@ -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 <http://www.gnu.org/licenses/>.
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
@ -62,32 +62,57 @@ Berlin 13359, Germany
</form>
</div>
<!-- LOGGED IN -->
<div class="lift:Login.loggedIn">
<div class="profile-info">
<div class="lift:Login.loggedIn">
<div class="profile-info">
Logged in as <span class="username">username@domain.com</span>
<a href="" class="logout">Logout</a>
</div>
</div>
</div>
</div>
</div>
<div id="nav">
<ul>
<li class="lift:Nav.item?name=Home navitem">
<a class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
</li>
<li class="lift:Nav.management navitem">
<a class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
</li>
<li class="lift:Nav.privilegeAdmin navitem">
<a class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
</li>
<span class="lift:Nav.views">
<li class="navitem">
<a class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
</li>
</span>
<li class="lift:Nav.onlyOnSomePages?pages=Home,Consumer%20Registration&name=Consumer%20Registration navitem">
<a class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
</li>
<li class="lift:Nav.onlyOnSomePages?pages=Home,Metrics&name=Metrics navitem">
<a class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
</li>
<li class="lift:Nav.onlyOnSomePages?pages=Home,Connect&name=Connect navitem">
<a class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
</li>
</ul>
</div>
<div id="content">
<lift:bind name="content" />
The main content gets bound here
The main content gets bound here
</div>
<div id="footer">
<p><a href="http://openbankproject.com">Open Bank Project</a> is &copy;2011-2012 <a href="http://tesobe.com">TESOBE</a> and distributed under the Apache 2.0 License.</p>
<br/>
<p><a href="http://openbankproject.com">Open Bank Project</a> is &copy;2011-2013 <a href="http://tesobe.com">TESOBE</a> and distributed under the Apache 2.0 License.</p>
<br/>
<p>
<a href="http://www.facebook.com/pages/Open-Bank-Project/118851468190327">Facebook Page</a> |
<a href="http://twitter.com/#!/OpenBankProject">Follow us on Twitter</a> |
<a href="http://eviscape.com/profile/openbankproject/">Eviscape Profile</a> |
<a href="http://polarize.it/polarize/openbankprojectdemoa_78828874370">Polarize it!</a> |
<a href="https://github.com/OpenBankProject">Sources on Github</a> |
<a href="https://github.com/OpenBankProject/OBP-API/wiki/OBP-Public-Facing-REST-API-V1.0-DRAFT">
RESTful API
</a>
</p>
<a href="http://www.facebook.com/pages/Open-Bank-Project/118851468190327">Facebook Page</a> |
<a href="http://twitter.com/#!/OpenBankProject">Follow us on Twitter</a> |
<a href="http://eviscape.com/profile/openbankproject/">Eviscape Profile</a> |
<a href="http://polarize.it/polarize/openbankprojectdemoa_78828874370">Polarize it!</a> |
<a href="https://github.com/OpenBankProject">Sources on Github</a> |
<a href="https://github.com/OpenBankProject/OBP-API/wiki">API Documentation</a>
</p>
</div>
</div>
<a id="feedBack" href="http://polarize.it/polarize/openbankprojectdemoa_78828874370"></a>

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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()
}

53
README
View File

@ -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".