├── .gitignore ├── CONTRIBUTING.md ├── GNU_AFFERO_GPL_V3_19_Nov_1997.txt ├── Harmony_Individual_Contributor_Assignment_Agreement.txt ├── NOTICE ├── README.md ├── mvn.sh ├── mvn_clean.sh ├── pom.xml ├── project ├── build.properties ├── build.scala └── plugins.sbt └── src ├── main ├── resources │ ├── .keep │ ├── cert │ │ ├── server.jks │ │ └── server.trust.jks │ ├── default.logback.xml │ ├── i18n │ │ ├── lift-core.properties │ │ ├── lift-core_en_GB.properties │ │ ├── lift-core_it_IT.properties │ │ └── lift-core_uk_UA.properties │ ├── props │ │ └── sample.props.template │ └── test.logback.xml ├── scala │ ├── bootstrap │ │ └── liftweb │ │ │ └── Boot.scala │ └── code │ │ ├── Constant.scala │ │ ├── lib │ │ ├── OAuthClient.scala │ │ ├── ObpAPI.scala │ │ ├── ObpOAuthProvider.scala │ │ └── SSLHelper.scala │ │ ├── snippet │ │ ├── AccountSettings.scala │ │ ├── AccountsOverview.scala │ │ ├── Comments.scala │ │ ├── CreateBankAccount.scala │ │ ├── CreateExpenditure.scala │ │ ├── CreateIncome.scala │ │ ├── CreatePermissionForm.scala │ │ ├── CustomEditable.scala │ │ ├── Dashboard.scala │ │ ├── DashboardAccountOverview.scala │ │ ├── Login.scala │ │ ├── Management.scala │ │ ├── Nav.scala │ │ ├── PermissionManagement.scala │ │ ├── TimeHelpers.scala │ │ ├── TransactionsList.scala │ │ ├── ViewsOverview.scala │ │ └── WebUI.scala │ │ ├── util │ │ ├── ExceptionLogger.scala │ │ ├── Helper.scala │ │ └── Util.scala │ │ └── widgets │ │ └── CustomTableSorter.scala └── webapp │ ├── 404.html │ ├── WEB-INF │ └── web.xml │ ├── about.html │ ├── banks │ └── star │ │ └── accounts │ │ ├── create-bank-account.html │ │ └── star │ │ ├── account-overview-dashboard.html │ │ ├── create-expenditure.html │ │ ├── create-income.html │ │ ├── management.html │ │ ├── permissions.html │ │ ├── permissions │ │ └── create.html │ │ ├── settings.html │ │ ├── star.html │ │ ├── transactions │ │ └── star │ │ │ └── star.html │ │ └── views │ │ └── list.html │ ├── correlated-user.html │ ├── dd.html │ ├── debug-info.html │ ├── favicon.ico │ ├── font-awesome │ ├── css │ │ ├── font-awesome.css │ │ └── font-awesome.min.css │ └── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── help.html │ ├── index.html │ ├── list-accounts.html │ ├── media │ ├── css │ │ ├── highlight.js.min.css │ │ ├── jquery.dropdown.min.css │ │ ├── normalize.css │ │ ├── reset.css │ │ ├── screen.css │ │ ├── style.css │ │ ├── toastr.min.css │ │ └── website.css │ ├── images │ │ ├── OBP_full_web.png │ │ ├── OBP_logo_simple.png │ │ ├── OpenBankProject_logo.jpg │ │ ├── add-off.png │ │ ├── add-on.png │ │ ├── ajax-loader.gif │ │ ├── arrow-right.png │ │ ├── blank.gif │ │ ├── cancel.png │ │ ├── close-icon.png │ │ ├── comment.png │ │ ├── edit-off.png │ │ ├── edit-on.png │ │ ├── favicon.ico │ │ ├── feedback-button.png │ │ ├── header.png │ │ ├── home-table-enter-icon.png │ │ ├── home-table-exit-icon.png │ │ ├── home-table-paper-icon.png │ │ ├── home-table-quote-icon.png │ │ ├── login-icon.png │ │ ├── logo-footer.png │ │ ├── logo-header.png │ │ ├── logo.png │ │ ├── logout-icon.png │ │ ├── money-in-icon.png │ │ ├── money-out-icon.png │ │ ├── moreInfo.png │ │ ├── nav-item.png │ │ ├── nav-selected.png │ │ ├── nav.png │ │ ├── polarize-logo.png │ │ ├── remove.png │ │ ├── select-arrow-home.png │ │ ├── select-arrow.png │ │ ├── settings-icon.png │ │ ├── sort.png │ │ ├── submit.png │ │ ├── transaction-in.png │ │ ├── transaction-out.png │ │ ├── upload-off.png │ │ └── upload-on.png │ └── js │ │ ├── highlight.min.js │ │ ├── notifications.js │ │ ├── polarize.js │ │ ├── scripts.js │ │ ├── toastr.min.js │ │ ├── vendor │ │ ├── jquery.dropdown.min.js │ │ ├── jquery.js │ │ └── jquery.tablesorter.min.js │ │ ├── views.js │ │ └── website.js │ ├── my-accounts.html │ ├── oauthcallback.html │ └── templates-hidden │ ├── _comment.html │ ├── _dashboard_account.html │ ├── _nav_account_settings.html │ ├── _tag.html │ ├── _transactionImage.html │ └── default.html └── test ├── resources └── .keep └── scala ├── LiftConsole.scala ├── RunMtlsWebApp.scala └── RunWebApp.scala /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.db 3 | .DS_Store 4 | *.scala.orig 5 | *.orig 6 | *.log 7 | .gitignore 8 | .idea 9 | .settings 10 | .classpath 11 | .project 12 | .cache 13 | src/main/resources/rebel.xml 14 | src/main/resources/props/* 15 | !src/main/resources/props/sample.props.template 16 | src/test/resources/props 17 | src/main/resources/git.properties 18 | 19 | src/main/webapp/WEB-INF/jetty.xml 20 | src/main/webapp/conf.html 21 | target/ 22 | *.iml 23 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 4 | ## Hello! 5 | 6 | Thank you for your interest in contributing to the Open Bank Project! 7 | 8 | ## Pull requests 9 | 10 | If submitting a pull request please read and sign our [CLA](http://github.com/OpenBankProject/OBP-API/blob/develop/Harmony_Individual_Contributor_Assignment_Agreement.txt) and send it to contact@tesobe.com - We'll send you back a code to include in the comment section of subsequent pull requests. 11 | 12 | Please reference Issue Numbers in your commits. 13 | 14 | ## Code comments 15 | 16 | Please comment your code ! :-) Imagine an engineer is trying to fix a production issue: she is working on a tiny screen, via a dodgy mobile Internet connection, in a sandstorm - Your code is fresh in your mind. Your comments could help her! 17 | 18 | ## Issues 19 | 20 | If would like to report an issue or suggest any kind of improvement please use Github Issues. 21 | 22 | ## Licenses 23 | 24 | Open Bank Project API, API Explorer and Sofi are dual licenced under the AGPL and commercial licenses. Open Bank Project SDKs are licenced under Apache 2 or MIT style licences. 25 | 26 | Please see the NOTICE for each project licence. 27 | 28 | ## Setup and Tests 29 | 30 | See the README for instructions on setup :-) 31 | 32 | Welcome! -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Open Bank Project - Sofi Web Application 2 | Copyright (C) 2011, 2012, TESOBE GmbH. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU Affero General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | 14 | You should have received a copy of the GNU Affero General Public License 15 | along with this program. If not, see . 16 | 17 | Email: contact@tesobe.com 18 | TESOBE GmbH. 19 | Osloer Str. 16/17 20 | Berlin 13359, Germany 21 | 22 | This product includes software developed at 23 | TESOBE (http://www.tesobe.com/) 24 | by 25 | Simon Redfern : simon AT tesobe DOT com 26 | Stefan Bethge : stefan AT tesobe DOT com 27 | Everett Sochowski : everett AT tesobe DOT com 28 | Ayoub Benali: ayoub AT tesobe DOT com 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Welcome to Sofit 2 | ===================== 3 | 4 | # ABOUT 5 | 6 | The Sofit application demonstrates some of the social aims behind the Open Bank Project: 7 | 8 | 1) Sliding scale of privacy and disclosure. e.g. Use aliases to protect real names but still show the flow of money. 9 | 2) Different views on account data (Public / Share holders / Team etc.) e.g. hide balances on public view. 10 | 3) Comment on transactions 11 | 4) Add other metadata e.g. tags / images to transactions / payees. 12 | 13 | 14 | # LICENSE 15 | 16 | This project is licensed under the AGPL V3 (see NOTICE). Commercial licences are available from TESOBE GmbH. 17 | 18 | # SETUP 19 | 20 | The project is using sbt or Maven 2 as a build tool. 21 | See build.scala or pom.xml respectively for the dependencies. 22 | 23 | ---- 24 | 25 | To compile and run jetty, cd into the root directory (where this file is) and run: 26 | 27 | $ sbt 28 | ... 29 | > compile 30 | > ~;container:start; container:reload / 31 | 32 | (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) 33 | 34 | In OS X, sbt can be installed with $ sudo port install sbt 35 | 36 | ---- 37 | 38 | 39 | Alternatively, maven can also be used: 40 | 41 | mvn jetty:run 42 | 43 | Note: You may need to add the pluginGroup to the $HOME/.m2/settings.xml 44 | 45 | 49 | ... 50 | 51 | org.mortbay.jetty 52 | 53 | ... 54 | 55 | 56 | --- 57 | 58 | ## Ubuntu 59 | 60 | If you use Ubuntu (or a derivative) and encrypted home directories (e.g. you have ~/.Private), you might run into the following error when the project is built: 61 | 62 | uncaught exception during compilation: java.io.IOException 63 | [ERROR] File name too long 64 | [ERROR] two errors found 65 | [DEBUG] Compilation failed (CompilerInterface) 66 | 67 | The current workaround is to move the project directory onto a different partition, e.g. under /opt/ . 68 | 69 | ## To run Sofit with IntelliJ IDEA 70 | 71 | 1. Make sure you have the IntelliJ Scala plugin installed. 72 | 2. Create a new folder e.g. OpenBankProject and cd there 73 | 3. git clone https://github.com/OpenBankProject/Sofit.git 74 | 4. In IntelliJ IDEA do File -> New -> Project from existing sources, navigate to the folder, and select pom.xml 75 | 5. If you see a message about an unmanaged pom.xml, click the option to let Maven manage it. 76 | 6. There is a props file template provided at src/main/resource/prop/sample.props.template. It needs to be renamed and modified or just copy and paste with a new file name for the application to work. 77 | 7.These change will be performe in default.props file (/src/main/resources/props/default.props). 78 | 79 | ## PROPS FILE 80 | 81 | 1. Copying and renaming: 82 | 83 | We suggest you copy and paste the sample.props.template file so you can refer to it later. 84 | The active Props file should be named *default.props* or another name that Liftweb accepts. See https://www.assembla.com/wiki/show/liftweb/Properties for the various options. 85 | 86 | Below are the important Props (see example.props.tempate for more info) 87 | 88 | ### *base_url* 89 | 90 | The base_url is used to calculate the callback url to give to the Open Bank Project API server. This should just be the 91 | base url used to access the social finance application. So if you're running a copy of the Sofit application at 92 | sofit.example.com over https, on the standard port, it would be "https://sofit.example.com". 93 | The suggested value for local development is: http://localhost:8081 but obviously you can choose a different non conflicting host:port 94 | 95 | ### *api_hostname* 96 | 97 | The api_hostname should be the base url (e.g. https://api.openbankproject.com) of the Open Bank Project API. \ 98 | If Sofit is running locally then define api_hostname as http://127.0.0.1:8080 in your props file. 99 | 100 | ### *obp_consumer_key* \ 101 | ### *obp_secret_key* 102 | 103 | You will need to get a consumer key and secret but registering a Consumer / Get API Key on the OBP API. 104 | See the README of OBP-API if you want to run OBP-API locally. 105 | 106 | 107 | #OBP API Setup 108 | 109 | Sofit needs some data setup for each User (Each user has their own bank and the user can create historical transactions at that bank) 110 | This is defined in OBP API function def sofitInitAction which runs after each user login. 111 | Sofit is also relying on using some Consumer Scopes (Entitlements that are set at the Consumer level not User level) 112 | 113 | Thus you will need to set the following *OBP API Props* (not Sofit!) 114 | sofit.logon_init_action.enabled=true 115 | allow_entitlements_or_scopes=true 116 | 117 | Also you will need to give your *Sofit Consumer* the following *Scopes* (using the OBP API Create Scope endpoint) 118 | 119 | CanCreateCustomerAtAnyBank 120 | CanCreateUserCustomerLinkAtAnyBank 121 | 122 | TODO: Consider extending sofit.login_init_action and grant CanCreateCustomer (at one bank) and CanCreateUserCustomerLink (at one bank) instead of relying on Scopes 123 | 124 | -------------------------------------------------------------------------------- /mvn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export MAVEN_OPTS="-Xmx512m -Xms512m -XX:MaxPermSize=256m" 4 | 5 | mvn $* 6 | -------------------------------------------------------------------------------- /mvn_clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mvn clean -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | #Project properties 2 | sbt.version=0.13.9 3 | -------------------------------------------------------------------------------- /project/build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import com.earldouglas.xwp._ 4 | import com.earldouglas.xwp.WebappPlugin 5 | import com.earldouglas.xwp.ContainerPlugin.autoImport._ 6 | 7 | 8 | object LiftProjectBuild extends Build { 9 | override lazy val settings = super.settings ++ buildSettings 10 | 11 | lazy val buildSettings = Seq( 12 | organization := pom.groupId, 13 | version := pom.version 14 | ) 15 | 16 | lazy val openBank = Project( 17 | pom.artifactId, 18 | base = file("."), 19 | settings = defaultSettings ++ pom.settings) 20 | .enablePlugins(JettyPlugin) 21 | 22 | object pom { 23 | 24 | val pomFile = "pom.xml" 25 | lazy val pom = xml.XML.loadFile(pomFile) 26 | 27 | lazy val pomProperties = (for{ 28 | props <- (pom \ "properties") 29 | p <- props.child 30 | } yield { 31 | p.label -> p.text 32 | }).toMap 33 | 34 | private lazy val PropertiesExpr = """.*\$\{(.*?)\}.*""".r 35 | 36 | def populateProps(t: String) = t match { 37 | case PropertiesExpr(p) => { 38 | val replaceWith = pomProperties.get(p) 39 | t.replace("${"+p+"}", replaceWith.getOrElse(throw new Exception("Cannot find property: '" + p + "' required by: '" + t + "' in: " + pomFile))) 40 | } 41 | case _ => t 42 | } 43 | 44 | lazy val pomDeps = (for{ 45 | dep <- pom \ "dependencies" \ "dependency" 46 | } yield { 47 | val scope = (dep \ "scope") 48 | val groupId = (dep \ "groupId").text 49 | val noScope = populateProps(groupId) % populateProps((dep \ "artifactId").text) % populateProps((dep \ "version").text) 50 | val nonCustom = if (scope.nonEmpty) noScope % populateProps(scope.text) 51 | else noScope 52 | 53 | if (groupId.endsWith("jetty")) Seq(noScope % "container", nonCustom) //hack to add jetty deps in container scope as it is required by the web plugin 54 | else Seq(nonCustom) 55 | }).flatten 56 | 57 | lazy val pomRepos = for { 58 | rep <- pom \ "repositories" \ "repository" 59 | } yield { 60 | populateProps((rep \ "url").text) at populateProps((rep \ "url").text) 61 | } 62 | 63 | lazy val pomScalaVersion = (pom \ "properties" \ "scala.compiler").text 64 | 65 | lazy val artifactId = (pom \ "artifactId").text 66 | lazy val groupId = (pom \ "groupId").text 67 | lazy val version = (pom \ "version").text 68 | lazy val name = (pom \ "name").text 69 | 70 | lazy val settings = Seq( 71 | scalaVersion := pomScalaVersion, 72 | libraryDependencies ++= pomDeps, 73 | resolvers ++= pomRepos, 74 | containerPort := 8081 75 | ) 76 | 77 | } 78 | 79 | lazy val defaultSettings = Defaults.defaultSettings ++ Seq( 80 | name := pom.name, 81 | resolvers ++= Seq( 82 | "Typesafe Repo" at "http://repo.typesafe.com/typesafe/releases", 83 | "Java.net Maven3 Repository" at "http://download.java.net/maven/3/", 84 | "Scala-Tools Dependencies Repository for Releases" at "http://scala-tools.org/repo-releases", 85 | "Scala-Tools Dependencies Repository for Snapshots" at "http://scala-tools.org/repo-snapshots"), 86 | 87 | // compile options 88 | scalacOptions ++= Seq("-encoding", "UTF-8", "-deprecation", "-unchecked", "-Xmax-classfile-name", "78"), 89 | javacOptions ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"), 90 | 91 | // show full stack traces 92 | testOptions in Test += Tests.Argument("-oF") 93 | ) 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += Classpaths.typesafeResolver 2 | 3 | // addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse" % "1.5.0") 4 | 5 | // resolvers += "sbt-deploy-repo" at "http://reaktor.github.com/sbt-deploy/maven" 6 | // addSbtPlugin("fi.reaktor" %% "sbt-deploy" % "0.3.1-SNAPSHOT") 7 | 8 | 9 | //xsbt-web-plugin 10 | addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "2.1.0") 11 | 12 | //resolvers += "Web plugin repo" at "http://siasia.github.com/maven2" 13 | //libraryDependencies <+= sbtVersion(v => v match { 14 | //case "0.11.0" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.0-0.2.8" 15 | //case "0.11.1" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.1-0.2.10" 16 | //case "0.11.2" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.2-0.2.11" 17 | //case "0.11.3" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.3-0.2.11.1" 18 | //}) 19 | 20 | //sbteclipse 21 | resolvers += { 22 | val typesafeRepoUrl = new java.net.URL("http://repo.typesafe.com/typesafe/releases") 23 | val pattern = Patterns(false, "[organisation]/[module]/[sbtversion]/[revision]/[type]s/[module](-[classifier])-[revision].[ext]") 24 | Resolver.url("Typesafe Repository", typesafeRepoUrl)(pattern) 25 | } 26 | 27 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0") 28 | addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2") 29 | 30 | //sbt-idea 31 | resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/" 32 | 33 | addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0") 34 | -------------------------------------------------------------------------------- /src/main/resources/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/resources/.keep -------------------------------------------------------------------------------- /src/main/resources/cert/server.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/resources/cert/server.jks -------------------------------------------------------------------------------- /src/main/resources/cert/server.trust.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/resources/cert/server.trust.jks -------------------------------------------------------------------------------- /src/main/resources/default.logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{yyyy-MM-dd HH:mm:ss} %t %c{0} [%p] %m%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/i18n/lift-core.properties: -------------------------------------------------------------------------------- 1 | login = Login 2 | logout = Logout 3 | views = Views 4 | private_accounts = Private accounts 5 | public_accounts = Public accounts 6 | my_accounts = My accounts 7 | account_name = Account Name 8 | you_are_logged_out = You are logged out. No authorised accounts available. 9 | enter_label = Enter a label... 10 | create_account_label = Create new account 11 | bank_name = Bank name 12 | #account_label = Account label 13 | account_label = Account name 14 | income_description = Description 15 | income_amount = Amount \u20ac 16 | payment_description = Description 17 | payment_amount = Amount \u20ac 18 | payment_category = Category 19 | income = Income 20 | payment = Expenditure 21 | expenditure = Expenditure 22 | add_expenditure = Add expenditure 23 | add_income = Add income 24 | button.save = Save 25 | button_cancel = Cancel 26 | button_show = Show 27 | api_documentation = API Documentation 28 | forgotten_password = Forgotten password? 29 | change_password = Change password 30 | new_label=Label 31 | expenditure.tags=Other,\ 32 | Telco&Internet,\ 33 | Family care,\ 34 | Insurance,\ 35 | Health,\ 36 | Restaurants,\ 37 | Spare time,\ 38 | Off line shopping,\ 39 | Transport,\ 40 | Rent and house,\ 41 | On-line shopping,\ 42 | Groceries 43 | income.tags=Salary,\ 44 | Transfer,\ 45 | Refund,\ 46 | Cash income,\ 47 | Pensions and yields,\ 48 | Extra and gifts 49 | months_ago = Period 50 | about = About 51 | about_info = Welcome to the IncludiMI App. IncludiMI is an innovative project which aims to provide financial identity to the underbanked. IncludiMI is led by Experian, Associazione Microfinanza e Sviluppo ONLUS, Associazione MicroLab and TESOBE. 52 | help_info = If you wish to access or amend any Personal Data we hold about you, or to request that we delete, export or transfer any information about you, you may email us with your request at dpoitaly@experian.com 53 | help = Help 54 | balance = Balance 55 | balance_colon = Balance: 56 | add_tag = ADD TAG 57 | add_image = ADD IMAGE 58 | add_comment = ADD COMMENT 59 | images = Images 60 | add_a_new_image = Add a new iage 61 | tags = Tags 62 | comments = Comments 63 | add_a_comment = Add a comment here 64 | months_ago_list = 1::month,2::months,3::months,4::months,5::months,6::months,7::months,8::months,9::months,10::months,11::months,12::months 65 | profile_completeness = Profile completeness 66 | credit_score = Credit score 67 | source_code_text = The source code for this application is available here: 68 | debug_info_text = Data we store related to your account includes the following: 69 | -------------------------------------------------------------------------------- /src/main/resources/i18n/lift-core_en_GB.properties: -------------------------------------------------------------------------------- 1 | login = Login 2 | logout = Logout 3 | views = Views 4 | private_accounts = Private accounts 5 | public_accounts = Public accounts 6 | my_accounts = My accounts 7 | account_name = Account Name 8 | you_are_logged_out = You are logged out. No authorised accounts available. 9 | enter_label = Enter a label... 10 | create_account_label = Create an account 11 | bank_name = Bank name 12 | account_label = Account name 13 | income_description = Description 14 | income_amount = Amount \u20ac 15 | payment_description = Description 16 | payment_amount = Amount \u20ac 17 | income = Income 18 | payment = Expenditure 19 | add_expenditure = Add expenditure 20 | button.save = Save 21 | api_documentation = API Documentation 22 | forgotten_password = Forgotten password? 23 | change_password = Change password 24 | new_label=Label 25 | expenditure.tags=Other,\ 26 | Telco&Internet,\ 27 | Family care,\ 28 | Insurance,\ 29 | Health,\ 30 | Restaurants,\ 31 | Spare time,\ 32 | Off line shopping,\ 33 | Transport,\ 34 | Rent and house,\ 35 | On-line shopping,\ 36 | Groceries 37 | income.tags=Salary,\ 38 | Transfer,\ 39 | Refund,\ 40 | Cash income,\ 41 | Pensions and yields,\ 42 | Extra and gifts -------------------------------------------------------------------------------- /src/main/resources/i18n/lift-core_it_IT.properties: -------------------------------------------------------------------------------- 1 | login = Accedi 2 | logout = Esci 3 | views = Visualizzazioni 4 | private_accounts = Conti privati 5 | public_accounts = Conti pubblici 6 | my_accounts = I miei registri 7 | account_name = Nome utente 8 | you_are_logged_out = Sei disconnesso. Nessun account autorizzato disponibile. 9 | enter_label = Crea un tag 10 | create_account_label = Crea un conto in banca 11 | bank_name = nome della banca 12 | account_label = Etichetta dell'account 13 | income_description = Descrizione del reddito 14 | income_amount = Importo del reddito 15 | payment_description = Descrizione 16 | payment_amount = Importo del pagamento 17 | payment_category = Categoria 18 | income = Reddito 19 | payment = Pagamento 20 | expenditure = Spesa 21 | add_expenditure = Aggiungi una spesa 22 | add_income = Aggiungi un'entrata 23 | button.save = Salva 24 | button_cancel = Annulla 25 | button_show = Vedi 26 | api_documentation = Documentazione API 27 | forgotten_password = Hai dimenticato la password? 28 | change_password = Cambia la password 29 | expenditure.tags=Spesa alimentari,\ 30 | Telefono e Internet,\ 31 | Spese familiari,\ 32 | Assicurazioni,\ 33 | Spese mediche,\ 34 | Ristorante,\ 35 | Tempo libero,\ 36 | Acquisiti in negozio,\ 37 | Trasporti,\ 38 | Casa,\ 39 | Acquisti on line,\ 40 | Extra e imprevisti 41 | income.tags=Stipendio,\ 42 | Bonifico,\ 43 | Rimborso spese,\ 44 | Entrate in contanti,\ 45 | Rendita e pensione,\ 46 | Extra e regali 47 | months_ago = Periodo 48 | about = Chi siamo 49 | help_info = Se desiderate accedere o modificare i vostri dati personali o richiedere la cancellazione, l'esportazione o il trasferimento delle informazioni che vi riguardano, potete inviarci un'e-mail con la vostra richiesta a gdpr@microfinanza.it 50 | balance = Bilancio 51 | balance_colon = Bilancio: 52 | add_tag = aggiungi un tag 53 | add_image = aggiungi un'immagine 54 | add_comment = aggiungi un commento 55 | images = Immagini 56 | add_a_new_image = aggiungi una nuova immagine 57 | tags = Tag 58 | comments = Commenti 59 | add_a_comment = Aggiungi un commento qui 60 | months_ago_list = 1::mese,2::mesi,3::mesi,4::mesi,5::mesi,6::mesi,7::mesi,8::mesi,9::mesi,10::mesi,11::mesi,12::mesi 61 | profile_completeness = Completezza del profilo 62 | credit_score = Punteggio di credito -------------------------------------------------------------------------------- /src/main/resources/i18n/lift-core_uk_UA.properties: -------------------------------------------------------------------------------- 1 | # The main drawback here is that all application resources for a given locale must exist 2 | # in the same file, and that extended characters such as œ aren’t properly encoded by 3 | # default. This is due to Java properties bundles using ISO 8859-1 encoding and thus 4 | # requiring conversion to escaped format, such as \u0153. Fortunately this process can 5 | # be automated using tools like native2ascii 6 | # https://native2ascii.net/ 7 | # This tool will allow you to convert national language characters to and from their Unicode equivalents in plain ASCII text. 8 | login = \u0443\u0432\u0456\u0439\u0442\u0438 9 | logout = \u0412\u0438\u0439\u0442\u0438 10 | views = \u041f\u0435\u0440\u0435\u0433\u043b\u044f\u0434\u0438 11 | private_accounts = \u041f\u0440\u0438\u0432\u0430\u0442\u043d\u0456 \u0440\u0430\u0445\u0443\u043d\u043a\u0438 12 | public_accounts = \u041f\u0443\u0431\u043b\u0456\u0447\u043d\u0456 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0456 \u0437\u0430\u043f\u0438\u0441\u0438 13 | my_accounts = \u041c\u043e\u0457 \u0440\u0430\u0445\u0443\u043d\u043a\u0438 14 | account_name = \u041d\u0430\u0437\u0432\u0430 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0438\u0441\u0443 15 | you_are_logged_out = \u0412\u0438 \u0432\u0438\u0439\u0448\u043b\u0438 \u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u0438. \u041d\u0435\u043c\u0430\u0454 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0438\u0445 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u0438\u0445 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0445 \u0437\u0430\u043f\u0438\u0441\u0456\u0432. 16 | enter_label = \u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043c\u0456\u0442\u043a\u0443 ... 17 | create_account_label = \u0421\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 18 | bank_name = \u043d\u0430\u0437\u0432\u0430 \u0431\u0430\u043d\u043a\u0443 19 | account_label = \u041c\u0456\u0442\u043a\u0430 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0438\u0441\u0443 20 | income_description = \u041e\u043f\u0438\u0441 21 | income_amount = \u0421\u0443\u043c\u0430 \u20ac 22 | payment_description = \u041e\u043f\u0438\u0441 23 | payment_amount = \u0421\u0443\u043c\u0430 \u20ac 24 | income = \u0414\u043e\u0445\u0456\u0434 25 | payment = \u0412\u0438\u0442\u0440\u0430\u0442\u0438 26 | add_expenditure = \u0414\u043e\u0434\u0430\u0439\u0442\u0435 \u0432\u0438\u0442\u0440\u0430\u0442\u0438 27 | button.save = \u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 28 | api_documentation = \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0456\u044f API 29 | forgotten_password = \u0417\u0430\u0431\u0443\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c? 30 | change_password = \u0417\u043c\u0456\u043d\u0438\u0442\u0438 \u043f\u0430\u0440\u043e\u043b\u044c 31 | expenditure.tags=\u0411\u0430\u043a\u0430\u043b\u0456\u044f,\ 32 | \u0422\u0435\u043b\u0435\u043a\u043e\u043c&\u0406\u043d\u0442\u0435\u0440\u043d\u0435\u0442,\ 33 | \u0421\u0456\u043c\u0435\u0439\u043d\u0438\u0439 \u0434\u043e\u0433\u043b\u044f\u0434,\ 34 | \u0421\u0442\u0440\u0430\u0445\u0443\u0432\u0430\u043d\u043d\u044f,\ 35 | \u0417\u0434\u043e\u0440\u043e\u0432'\u044f,\ 36 | \u0420\u0435\u0441\u0442\u043e\u0440\u0430\u043d\u0438,\ 37 | \u0412\u0456\u043b\u044c\u043d\u0438\u0439 \u0447\u0430\u0441,\ 38 | \u041f\u043e\u043a\u0443\u043f\u043a\u0438 \u043e\u0444\u043b\u0430\u0439\u043d,\ 39 | \u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442,\ 40 | \u041e\u0440\u0435\u043d\u0434\u0430 \u0442\u0430 \u0431\u0443\u0434\u0438\u043d\u043e\u043a,\ 41 | \u0406\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0438,\ 42 | \u0406\u043d\u0448\u0438\u0439 43 | income.tags=\u0417\u0430\u0440\u043e\u0431\u0456\u0442\u043d\u0430 \u043f\u043b\u0430\u0442\u0430,\ 44 | \u041f\u0435\u0440\u0435\u0434\u0430\u0447\u0430,\ 45 | \u0412\u0456\u0434\u0448\u043a\u043e\u0434\u0443\u0432\u0430\u043d\u043d\u044f,\ 46 | \u0413\u0440\u043e\u0448\u043e\u0432\u0438\u0439 \u0434\u043e\u0445\u0456\u0434,\ 47 | \u041f\u0435\u043d\u0441\u0456\u0457 \u0442\u0430 \u0432\u0440\u043e\u0436\u0430\u0457,\ 48 | \u0414\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u043e \u0456 \u043f\u043e\u0434\u0430\u0440\u0443\u043d\u043a\u0438 -------------------------------------------------------------------------------- /src/main/resources/props/sample.props.template: -------------------------------------------------------------------------------- 1 | #this is a sample props file you should edit and rename 2 | #see https://www.assembla.com/wiki/show/liftweb/Properties for all the naming options, or just use "default.props" in this same folder 3 | 4 | 5 | ###################################### 6 | ## REQUIRED ########################## 7 | 8 | # The base url of the api to use (e.g. https://YOUR-OBP-API-HOST.com or http://127.0.0.1:8080 etc.) 9 | api_hostname=FILL_ME_IN 10 | 11 | # At the moment Sofit doesn't have the ability select multiple user auth providers 12 | # In most cases, this should just be identical to "api_hostname" 13 | # If not filled in you will get a page of red box errors! 14 | defaultAuthProvider=FILL_ME_IN 15 | 16 | # These are the oauth keys obtained from the api (at /consumer-registration) 17 | obp_consumer_key=FILL_ME_IN 18 | obp_secret_key=FILL_ME_IN 19 | 20 | # The base url / redirect url of the of this application 21 | # This is used for the oauth callback url. 22 | # When you register your consumer key for this application on the API, use this for redirect_url 23 | # Note: If you are running a local API instance and a local Sofit instance on the same machine, we *strongly* recommend that you call the API on 127.0.0.1 in your browser and Sofi on localhost to avoid weird cookie issues 24 | # Suggested value for local testing is: http://localhost:8081 25 | base_url=FILL_ME_IN 26 | 27 | ## End of REQUIRED ################### 28 | ###################################### 29 | 30 | 31 | #this is only useful for running the api locally via RunWebApp 32 | #if you use it, make sure this matches your base_url port! 33 | #if you want to change the port when running via the command line, use "mvn -Djetty.port=8081 jetty:run" instead 34 | dev.port=8081 35 | 36 | 37 | 38 | #Your transloadit auth key (used to upload transaction images) 39 | #not needed unless you want to upload images 40 | transloadit.authkey=FILL_ME_IN 41 | 42 | #Your transloadit template used to process transaction image uploading 43 | #not needed unless you want to upload images 44 | transloadit.addImageTemplate=FILL_ME_IN 45 | 46 | 47 | # Link to the API Explorer 48 | webui_api_explorer_url= 49 | 50 | # API version 51 | # Default value is v4.0.0 52 | api_version=v4.0.0 53 | 54 | # Sngle Sign On 55 | # sso.enabled=false 56 | # Left logo at the header 57 | webui_header_logo_left_url=/media/images/logo-header.png 58 | 59 | ### OBP-API mode ############################## 60 | # If OBP-API split to two instances, eg: apis,portal 61 | # Then API_Explorer need to set two api hosts: api_hostname and this api_portal_hostname, for all Rest Apis will call api_hostname 62 | # but for all the portal home page link, we need to use this props. If do not set this, it will use api_hostname value instead. 63 | # api_portal_hostname=http://127.0.0.1:8080 64 | # -------------------------------------------- 65 | 66 | # Enable/Disable features of the application 67 | management.counterparties.enabled=true 68 | management.views.enabled=true 69 | management.users.enabled=true 70 | management.create_account.enabled=false 71 | management.update_account_label.enabled=false 72 | 73 | # Limit the System views displayed by Sofit. 74 | sytems_views_to_display=owner,accountant,auditor 75 | 76 | # Limit the System views displayed by Sofit. 77 | hide_public_accounts_panel=false 78 | 79 | # Show/Hide Twitter link at the footer. 80 | display_twitter_link=true 81 | 82 | # Show/Hide GitHub link at the footer. 83 | display_github_link=true 84 | 85 | # Show/Hide API documentation link at the footer. 86 | display_api_docs_link=true 87 | 88 | # Set Locale 89 | language_tag = en-GB 90 | 91 | # Incoming account at manual_transaction_bank_id 92 | incoming.account_id=OBP_DEFAULT_INCOMING_ACCOUNT_ID 93 | 94 | # Outgoing account at manual_transaction_bank_id 95 | outgoing.account_id=OBP_DEFAULT_OUTGOING_ACCOUNT_ID 96 | 97 | # Display link to the counterpart account 98 | display_other_account_link_at_transaction=true 99 | 100 | # Help page documentation link 101 | documentation_link = FILL_ME_IN 102 | 103 | # If you want to make the Lift inactivity timeout shorter than 104 | # the container inactivity timeout, set the inactivity timeout here 105 | session_inactivity_timeout_in_minutes = 30 106 | 107 | # EULA 108 | eula_url=https://www.example.com 109 | eula_text=End User Licence Agreement & Privacy Policy 110 | 111 | # Source coe url 112 | source_code_url=https://github.com/OpenBankProject/Sofit -------------------------------------------------------------------------------- /src/main/resources/test.logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{yyyy-MM-dd HH:mm:ss} %t %c{0} [%p] %m%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/scala/code/Constant.scala: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | import net.liftweb.util.Props 4 | 5 | object Constant { 6 | final val CUSTOM_OWNER_VIEW_ID = "owner" 7 | final val versionOfApi = Props.get("api_version").getOrElse("v4.0.0") 8 | final val versionOfApi121 = "v1.2.1" 9 | final val correlatedUserIdTargetCookieName = "CORRELATED_USER_ID_TARGET" 10 | final val correlatedUserIdBoundCookieName = "CORRELATED_USER_ID_BOUND" 11 | final val correlatedCustomerIdCreatedCookieName = "CORRELATED_CUSTOMER_ID_CREATED" 12 | final val linkBetweenCorrelatedUserAndCustomerCreatedCookieName = "LINK_BETWEEN_CORRELATED_USER_AND_CUSTOMER_CREATED" 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/code/lib/OAuthClient.scala: -------------------------------------------------------------------------------- 1 | /** 2 | Open Bank Project - Sofi Web Application 3 | Copyright (C) 2011 - 2021, TESOBE GmbH. 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | 18 | Email: contact@tesobe.com 19 | TESOBE GmbH. 20 | Osloer Str. 16/17 21 | Berlin 13359, Germany 22 | 23 | This product includes software developed at 24 | TESOBE (http://www.tesobe.com/) 25 | by 26 | Simon Redfern : simon AT tesobe DOT com 27 | Stefan Bethge : stefan AT tesobe DOT com 28 | Everett Sochowski : everett AT tesobe DOT com 29 | Ayoub Benali: ayoub AT tesobe DOT com 30 | 31 | */ 32 | 33 | package code.lib 34 | 35 | import code.util.Helper 36 | import code.util.Helper.MdcLoggable 37 | import net.liftweb.common.{Box, Empty, Failure, Full} 38 | import net.liftweb.http.{LiftResponse, S, SessionVar} 39 | import net.liftweb.util.Props 40 | import oauth.signpost.{OAuthConsumer, OAuthProvider} 41 | import oauth.signpost.basic.DefaultOAuthConsumer 42 | import oauth.signpost.signature.HmacSha256MessageSigner 43 | 44 | sealed trait Provider { 45 | val name : String 46 | 47 | val apiBaseUrl : String 48 | val requestTokenUrl : String 49 | val accessTokenUrl : String 50 | val authorizeUrl : String 51 | val signupUrl : Option[String] 52 | 53 | /** 54 | * Can't do oAuthProvider = new DefaultOAuthProvider(requestTokenUrl, accessTokenUrl, authorizeUrl) 55 | * here as the Strings all evaluate at null at this point in object creation 56 | */ 57 | val oAuthProvider : OAuthProvider 58 | 59 | val consumerKey : String 60 | val consumerSecret : String 61 | } 62 | 63 | trait DefaultProvider extends Provider { 64 | val name = "The Open Bank Project Demo" 65 | 66 | val baseUrl = Props.get("api_hostname", S.hostName) 67 | val oauthBaseUrlPortal = Props.get("api_portal_hostname").getOrElse(baseUrl) 68 | val apiBaseUrl = baseUrl + "/obp" 69 | val requestTokenUrl = baseUrl + "/oauth/initiate" 70 | val accessTokenUrl = baseUrl + "/oauth/token" 71 | val authorizeUrl = oauthBaseUrlPortal + "/oauth/authorize" 72 | val signupUrl = Some(baseUrl + "/user_mgt/sign_up") 73 | 74 | lazy val oAuthProvider : OAuthProvider = new ObpOAuthProvider(requestTokenUrl, accessTokenUrl, authorizeUrl) 75 | 76 | val consumerKey = Props.get("obp_consumer_key", "") 77 | val consumerSecret = Props.get("obp_secret_key", "") 78 | } 79 | 80 | object OBPDemo extends DefaultProvider 81 | 82 | object AddBankAccountProvider extends DefaultProvider { 83 | override val name = "The Open Bank Project Demo - Add Bank Account" 84 | 85 | //The "login" prefix before /oauth means that we will use the oauth flow that will ask the user 86 | //to connect a bank account 87 | override val requestTokenUrl = baseUrl + "/login/oauth/initiate" 88 | override val accessTokenUrl = baseUrl + "/login/oauth/token" 89 | override val authorizeUrl = baseUrl + "/login/oauth/authorize" 90 | } 91 | 92 | case class Consumer(consumerKey : String, consumerSecret : String) { 93 | val oAuthConsumer : OAuthConsumer = new DefaultOAuthConsumer(consumerKey, consumerSecret) 94 | } 95 | 96 | case class Credential(provider : Provider, consumer : OAuthConsumer, readyToSign : Boolean) 97 | 98 | object credentials extends SessionVar[Option[Credential]](None) 99 | object mostRecentLoginAttemptProvider extends SessionVar[Box[Provider]](Empty) 100 | 101 | object OAuthClient extends MdcLoggable { 102 | 103 | def getAuthorizedCredential() : Option[Credential] = { 104 | credentials.filter(_.readyToSign) 105 | } 106 | 107 | def currentApiBaseUrl : String = { 108 | getAuthorizedCredential().map(_.provider.apiBaseUrl).getOrElse(OBPDemo.apiBaseUrl) 109 | } 110 | 111 | def setNewCredential(provider : Provider) : Credential = { 112 | val consumer = new DefaultOAuthConsumer(provider.consumerKey, provider.consumerSecret) 113 | val credential = Credential(provider, consumer, false) 114 | 115 | credentials.set(Some(credential)) 116 | credential 117 | } 118 | 119 | def handleCallback(): Box[LiftResponse] = { 120 | 121 | val success = for { 122 | verifier <- S.param("oauth_verifier") ?~ "No oauth verifier found" 123 | provider <- mostRecentLoginAttemptProvider.get ?~ "No provider found for callback" 124 | consumer <- Box(credentials.map(_.consumer)) ?~ "No consumer found for callback" 125 | } yield { 126 | //after this, consumer is ready to sign requests 127 | provider.oAuthProvider.retrieveAccessToken(consumer, verifier) 128 | //update the session credentials 129 | val newCredential = Credential(provider, consumer, true) 130 | credentials.set(Some(newCredential)) 131 | } 132 | 133 | success match { 134 | case Full(_) => S.redirectTo("/") //TODO: Allow this redirect to be customised 135 | case Failure(msg, _, _) => logger.warn(msg) 136 | case _ => logger.warn("Something went wrong in an oauth callback and there was no error message set for it") 137 | } 138 | Empty 139 | } 140 | 141 | def redirectToOauthLogin() = { 142 | redirect(OBPDemo) 143 | } 144 | 145 | private def redirect(provider : Provider) = { 146 | mostRecentLoginAttemptProvider.set(Full(provider)) 147 | val credential = setNewCredential(provider) 148 | credential.consumer.setMessageSigner(new HmacSha256MessageSigner()) 149 | val authUrl = provider.oAuthProvider.retrieveRequestToken(credential.consumer, Props.get("base_url", S.hostName) + "/oauthcallback") 150 | S.redirectTo(authUrl) 151 | } 152 | 153 | def redirectToConnectBankAccount() = { 154 | redirect(AddBankAccountProvider) 155 | } 156 | 157 | def loggedIn : Boolean = credentials.map(_.readyToSign).getOrElse(false) 158 | 159 | def logoutAll() = { 160 | val apiExplorerHost = {Props.get("base_url", S.hostName)} 161 | val obpApiHost = { Props.get("api_portal_hostname").or(Props.get("api_hostname")).getOrElse("Unknown") } 162 | credentials.set(None) 163 | S.redirectTo(s"$obpApiHost/user_mgt/logout?redirect=$apiExplorerHost") 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/scala/code/lib/ObpOAuthProvider.scala: -------------------------------------------------------------------------------- 1 | package code.lib 2 | 3 | import java.io.IOException 4 | import java.net.{HttpURLConnection, MalformedURLException} 5 | 6 | import oauth.signpost.AbstractOAuthProvider 7 | import oauth.signpost.basic.{HttpURLConnectionRequestAdapter, HttpURLConnectionResponseAdapter} 8 | import oauth.signpost.http.{HttpRequest, HttpResponse} 9 | 10 | 11 | /** 12 | * Library https://github.com/mttkay/signpost we use for OAuth1 does not implement MTLS connection. 13 | * In order to support it ObpOAuthProvider inherit AbstractOAuthProvider and override function createRequest 14 | * in a way that {@link HttpURLConnection} is used in case the props ssl_client_auth=false 15 | * or {@link HttpsURLConnection} in case the props ssl_client_auth=true 16 | * to receive tokens from a service provider. 17 | */ 18 | @SerialVersionUID(1L) 19 | class ObpOAuthProvider(val requestTokenEndpointUrl: String, val accessTokenEndpointUrl: String, val authorizationWebsiteUrl: String) extends AbstractOAuthProvider(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl) { 20 | @throws[MalformedURLException] 21 | @throws[IOException] 22 | override protected def createRequest(endpointUrl: String): HttpRequest = { 23 | val connection = SSLHelper.getConnection(endpointUrl) 24 | connection.setRequestMethod("POST") 25 | connection.setAllowUserInteraction(false) 26 | connection.setRequestProperty("Content-Length", "0") 27 | new HttpURLConnectionRequestAdapter(connection) 28 | } 29 | 30 | @throws[IOException] 31 | override protected def sendRequest(request: HttpRequest): HttpResponse = { 32 | val connection = request.unwrap.asInstanceOf[HttpURLConnection] 33 | connection.connect() 34 | new HttpURLConnectionResponseAdapter(connection) 35 | } 36 | 37 | override protected def closeConnection(request: HttpRequest, response: HttpResponse): Unit = { 38 | val connection = request.unwrap.asInstanceOf[HttpURLConnection] 39 | if (connection != null) connection.disconnect() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/scala/code/lib/SSLHelper.scala: -------------------------------------------------------------------------------- 1 | package code.lib 2 | 3 | import java.io.FileInputStream 4 | import java.net.{HttpURLConnection, URL} 5 | import java.security.{KeyStore, SecureRandom} 6 | 7 | import javax.net.ssl.{TrustManagerFactory, _} 8 | import net.liftweb.util.Props 9 | 10 | object SSLHelper { 11 | 12 | 13 | private lazy val sSLSocketFactory = { 14 | val keystoreFile: String = Props.get("ssl_keystore_location").openOrThrowException("props value of ssl_keystore_location is missing") 15 | val keystorePassword = Props.get("ssl_keystore_password", "") 16 | val truststoreFile = Props.get("ssl_truststore_location","") 17 | val truststorePassword = Props.get("ssl_truststore_password", "") 18 | val keyPassword = Props.get("ssl_key_password", "") 19 | 20 | 21 | 22 | val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm) 23 | val keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm) 24 | val keystore = KeyStore.getInstance(KeyStore.getDefaultType) 25 | val inputStream = new FileInputStream(keystoreFile) 26 | try { 27 | keystore.load(inputStream, keystorePassword.toCharArray) 28 | } finally { 29 | inputStream.close() 30 | } 31 | keyManager.init(keystore,keyPassword.toCharArray) 32 | 33 | val truststore = KeyStore.getInstance(KeyStore.getDefaultType) 34 | val trustInputStream = new FileInputStream(truststoreFile) 35 | try { 36 | truststore.load(trustInputStream, truststorePassword.toCharArray) 37 | } finally { 38 | trustInputStream.close() 39 | } 40 | 41 | 42 | tmf.init(truststore) 43 | 44 | 45 | 46 | val sslContext = SSLContext.getInstance("TLSv1.2") 47 | sslContext.init(keyManager.getKeyManagers, tmf.getTrustManagers, new SecureRandom()) 48 | 49 | val hostnameVerifier: HostnameVerifier = new HostnameVerifier { 50 | override def verify(host: String, sslSession: SSLSession): Boolean = true 51 | } 52 | 53 | HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier) 54 | 55 | sslContext.getSocketFactory 56 | } 57 | 58 | def getConnection(url: String): HttpURLConnection = { 59 | Props.get("ssl_client_auth", "false") match { 60 | case "true" => { 61 | val httpsUrl = if (url.startsWith("https://")) url else url.replaceFirst("^http://", "https://") 62 | 63 | val connection = new URL(httpsUrl).openConnection().asInstanceOf[HttpsURLConnection] 64 | connection.setSSLSocketFactory(sSLSocketFactory) 65 | connection 66 | } 67 | case "false" => new URL(url).openConnection().asInstanceOf[HttpURLConnection] 68 | case errorValue => sys.error(s"obp_certificate_activate props should be true or false, current value is: $errorValue") 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/AccountSettings.scala: -------------------------------------------------------------------------------- 1 | package code.snippet 2 | 3 | import code.Constant._ 4 | import code.lib.ObpAPI.{getAccount, updateAccountLabel} 5 | import code.util.Helper.{MdcLoggable, getAccountTitle} 6 | import net.liftweb.http.SHtml 7 | import net.liftweb.http.js.JE.Call 8 | import net.liftweb.http.js.JsCmd 9 | import net.liftweb.http.js.JsCmds.SetHtml 10 | import net.liftweb.util.Helpers._ 11 | 12 | import scala.xml.{NodeSeq, Text} 13 | 14 | 15 | /* 16 | For maintaining permissions on the views (entitlements on the account) 17 | */ 18 | class AccountSettings(params: List[String]) extends MdcLoggable { 19 | val bankId = params(0) 20 | val accountId = params(1) 21 | val accountJson = getAccount(bankId, accountId, CUSTOM_OWNER_VIEW_ID).openOrThrowException("Could not open accountJson") 22 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson) 23 | 24 | //set up ajax handlers to edit account label 25 | def editLabel(xhtml: NodeSeq): NodeSeq = { 26 | var newLabel = "" 27 | 28 | def process(): JsCmd = { 29 | logger.debug(s"AccountSettings.editLabel.process: edit label $newLabel") 30 | val result = updateAccountLabel(bankId, accountId, newLabel) 31 | if (result.isDefined) { 32 | val msg = "Label " + newLabel + " has been set" 33 | SetHtml("account-title", Text(newLabel)) & 34 | Call("socialFinanceNotifications.notify", msg).cmd 35 | } else { 36 | val msg = "Sorry, Label" + newLabel + " could not be set ("+ result +")" 37 | Call("socialFinanceNotifications.notifyError", msg).cmd 38 | } 39 | } 40 | 41 | ( 42 | // Bind newViewName field to variable (e.g. http://chimera.labs.oreilly.com/books/1234000000030/ch03.html) 43 | "@new_label" #> SHtml.text(accountJson.label.getOrElse(""), s => newLabel = s) & 44 | // Replace the type=submit with Javascript that makes the ajax call. 45 | "type=submit" #> SHtml.ajaxSubmit("Save account label", process) 46 | ).apply(xhtml) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/CreateBankAccount.scala: -------------------------------------------------------------------------------- 1 | package code.snippet 2 | 3 | import code.lib.ObpAPI 4 | import code.lib.ObpAPI.createAccount 5 | import code.lib.ObpJson.BankJson400 6 | import code.util.Helper.MdcLoggable 7 | import net.liftweb.common.Box 8 | import net.liftweb.http.js.JE.Call 9 | import net.liftweb.http.js.JsCmd 10 | import net.liftweb.http.js.JsCmds.SetHtml 11 | import net.liftweb.http.{RequestVar, SHtml} 12 | import net.liftweb.util.Helpers._ 13 | 14 | import scala.collection.immutable.List 15 | import scala.xml.{NodeSeq, Text} 16 | 17 | 18 | class CreateBankAccount(params: List[BankJson400]) extends MdcLoggable { 19 | 20 | private object bankVar extends RequestVar("") 21 | private object userIdVar extends RequestVar("") 22 | 23 | 24 | def editLabel(xhtml: NodeSeq): NodeSeq = { 25 | var newLabel = "" 26 | val bankId = "user." + ObpAPI.currentUser.map(_.user_id).getOrElse(System.currentTimeMillis()) 27 | val listOfBanks = params 28 | .filter(_.id == bankId) 29 | .map(b => (b.id, b.full_name)) 30 | 31 | def process(): JsCmd = { 32 | ObpAPI.currentUser.map { 33 | u => userIdVar.set(u.user_id) 34 | } 35 | logger.debug(s"CreateBankAccount.editLabel.process: edit label $newLabel") 36 | if(listOfBanks.size == 1) { 37 | val result = createAccount(bankVar.is, newLabel, userIdVar.is) 38 | if (result.isDefined) { 39 | val msg = "Saved" 40 | SetHtml("account-title", Text(newLabel)) & 41 | Call("socialFinanceNotifications.notify", msg).cmd 42 | } else { 43 | val msg = "Sorry, the new account with the label" + newLabel + " could not be set ("+ result +")" 44 | Call("socialFinanceNotifications.notifyError", msg).cmd 45 | } 46 | } else { 47 | val msg = "Sorry, the new account with the label " + newLabel + " could not be set due to unresolved bank id value" 48 | Call("socialFinanceNotifications.notifyError", msg).cmd 49 | } 50 | } 51 | 52 | ( 53 | "@new_label" #> SHtml.text("", s => newLabel = s) & 54 | "#bank-id" #> SHtml.select(listOfBanks, Box!! bankVar.is, bankVar(_)) & 55 | // Replace the type=submit with Javascript that makes the ajax call. 56 | "type=submit" #> SHtml.ajaxSubmit("Create", process) 57 | ).apply(xhtml) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/CreateExpenditure.scala: -------------------------------------------------------------------------------- 1 | package code.snippet 2 | 3 | import code.Constant._ 4 | import code.lib.ObpAPI 5 | import code.lib.ObpAPI.{createOutcome, getAccount} 6 | import code.util.Helper.{MdcLoggable, getAccountTitle} 7 | import net.liftweb.common.Box 8 | import net.liftweb.http.js.JE.Call 9 | import net.liftweb.http.js.JsCmd 10 | import net.liftweb.http.{RequestVar, S, SHtml} 11 | import net.liftweb.util.Helpers._ 12 | 13 | import scala.xml.NodeSeq 14 | 15 | 16 | class CreateExpenditure(params: List[String]) extends MdcLoggable { 17 | private object tagVar extends RequestVar("") 18 | val bankId = params(0) 19 | val accountId = params(1) 20 | val accountJson = getAccount(bankId, accountId, CUSTOM_OWNER_VIEW_ID).openOrThrowException("Could not open accountJson") 21 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson) 22 | 23 | 24 | def addPayment(xhtml: NodeSeq): NodeSeq = { 25 | var outcomeDescription = "" 26 | var outcomeAmount = 0D 27 | val listOfTags: Seq[(String, String)] = S.?("expenditure.tags"). 28 | split(",").toList.map(_.trim).map(i => i.replaceAll("_", "/")).map(i => (i,i)) 29 | 30 | def process(): JsCmd = { 31 | logger.debug(s"CreateOutcome.addOutcome.process: edit label $outcomeDescription") 32 | val result = createOutcome(bankId, accountId, outcomeDescription, outcomeAmount.toString, "EUR") 33 | if (result.isDefined) { 34 | val transactionId = result.map(_.transaction_id).getOrElse("") 35 | val addTags = ObpAPI.addTags(bankId, accountId, "owner", transactionId, List(tagVar.is)) 36 | logger.debug(s"CreateOutcome.addOutcome.process.addTags $addTags") 37 | val msg = "Saved" 38 | Call("socialFinanceNotifications.notify", msg).cmd 39 | S.redirectTo(s"/banks/$bankId/accounts/$accountId/owner") 40 | } else { 41 | val msg = s"Sorry, Expenditure $outcomeDescription could not be added ($result)" 42 | Call("socialFinanceNotifications.notifyError", msg).cmd 43 | } 44 | } 45 | 46 | ( 47 | "@payment_description" #> SHtml.text("", s => outcomeDescription = s) & 48 | "#payment_category" #> SHtml.select(listOfTags, Box!! tagVar.is, tagVar(_)) & 49 | "@payment_amount" #> SHtml.number(0D, (s:Double) => outcomeAmount = s, 0D, 1000000000D, 0.01D) & 50 | // Replace the type=submit with Javascript that makes the ajax call. 51 | "type=submit" #> SHtml.ajaxSubmit(S.?("button.save"), process) 52 | ).apply(xhtml) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/CreateIncome.scala: -------------------------------------------------------------------------------- 1 | package code.snippet 2 | 3 | import code.Constant._ 4 | import code.lib.ObpAPI 5 | import code.lib.ObpAPI.{createIncome, getAccount, getBalancingTransaction} 6 | import code.util.Helper.{MdcLoggable, getAccountTitle} 7 | import net.liftweb.common.Box 8 | import net.liftweb.http.js.JE.Call 9 | import net.liftweb.http.js.JsCmd 10 | import net.liftweb.http.{RequestVar, S, SHtml} 11 | import net.liftweb.util.Helpers._ 12 | import net.liftweb.util.Props 13 | 14 | import scala.xml.NodeSeq 15 | 16 | 17 | class CreateIncome(params: List[String]) extends MdcLoggable { 18 | private object tagVar extends RequestVar("") 19 | val bankId = params(0) 20 | val accountId = params(1) 21 | val accountJson = getAccount(bankId, accountId, CUSTOM_OWNER_VIEW_ID).openOrThrowException("Could not open accountJson") 22 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson) 23 | 24 | //set up ajax handlers to edit account label 25 | def addIncome(xhtml: NodeSeq): NodeSeq = { 26 | var incomeDescription = "" 27 | var incomeAmount = 0D 28 | val listOfTags: Seq[(String, String)] = S.?("income.tags"). 29 | split(",").toList.map(_.trim).map(i => i.replaceAll("_", "/")).map(i => (i,i)) 30 | 31 | def process(): JsCmd = { 32 | logger.debug(s"CreateIncome.addIncome.process: edit label $incomeDescription") 33 | val result = createIncome(bankId, accountId, incomeDescription, incomeAmount.toString, "EUR") 34 | if (result.isDefined) { 35 | val incomeAccountId = Props.get("incoming.account_id", "") 36 | val transactionId = result.map(_.transaction_id).getOrElse("") 37 | logger.debug(s"CreateIncome.addIncome.process.transactionId $transactionId") 38 | val creditTransactionId = getBalancingTransaction(transactionId) 39 | .map(_.credit_transaction.transaction_id).getOrElse("") 40 | logger.debug(s"CreateIncome.addIncome.process.creditTransactionId $creditTransactionId") 41 | val addTags = ObpAPI.addTags(bankId, accountId, "owner", creditTransactionId, List(tagVar.is)) 42 | logger.debug(s"CreateIncome.addIncome.process.addTags $addTags") 43 | val msg = "Saved" 44 | Call("socialFinanceNotifications.notify", msg).cmd 45 | S.redirectTo(s"/banks/$bankId/accounts/$accountId/owner") 46 | } else { 47 | val msg = s"Sorry, Income $incomeDescription could not be added ($result)" 48 | Call("socialFinanceNotifications.notifyError", msg).cmd 49 | } 50 | } 51 | 52 | ( 53 | "@income_description" #> SHtml.text("", s => incomeDescription = s) & 54 | "#income_category" #> SHtml.select(listOfTags, Box!! tagVar.is, tagVar(_)) & 55 | "@income_amount" #> SHtml.number(0D, (s:Double) => incomeAmount = s, 0D, 1000000000D, 0.01D) & 56 | // Replace the type=submit with Javascript that makes the ajax call. 57 | "type=submit" #> SHtml.ajaxSubmit(S.?("button.save"), process) 58 | ).apply(xhtml) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/CreatePermissionForm.scala: -------------------------------------------------------------------------------- 1 | package code.snippet 2 | 3 | import code.lib.ObpAPI 4 | import code.lib.ObpJson.{AccountJson, ViewJson} 5 | import code.util.Helper.{MdcLoggable, getAccountTitle} 6 | import net.liftweb.common.Failure 7 | import net.liftweb.http.{S, SHtml} 8 | import net.liftweb.http.js.JsCmd 9 | import net.liftweb.http.js.JsCmds._ 10 | import net.liftweb.util.Helpers._ 11 | 12 | import scala.xml.Text 13 | 14 | 15 | class CreatePermissionForm(params : (List[ViewJson], AccountJson, PermissionsUrlParams)) extends MdcLoggable { 16 | case class ViewData(view : ViewJson, allowed: Boolean) 17 | 18 | val views = params._1 19 | val accountJson = params._2 20 | val urlParams = params._3 21 | 22 | val url = S.uri.split("/") 23 | var username = "" 24 | 25 | val allowed : scala.collection.mutable.Map[ViewJson, Boolean] = scala.collection.mutable.Map(views.map(v => (v, false)).toSeq : _*) 26 | 27 | val nonPublicViews = views.filterNot(_.is_public.getOrElse(true)) 28 | val nonBadViews = nonPublicViews.filterNot(_.id == None) 29 | def viewData = nonBadViews.map(view => ViewData(view, allowed(view))) 30 | 31 | def render = { 32 | 33 | def process(): JsCmd = { 34 | 35 | def showMsg(msg : String) = { 36 | SetHtml("create-permission-message", Text(msg)) 37 | } 38 | 39 | if(username.isEmpty()) showMsg("You must enter a username") 40 | //else if(invalidEmail()) showMsg("Invalid e-mail address") 41 | //else if(viewData.forall(vData => allowed(vData.view) == false)) showMsg("You must select at least one view to grant access to.") 42 | else if(viewData.forall(_.allowed == false)) showMsg("You must select at least one view to grant access to.") 43 | else { 44 | //create the permission and return any failure 45 | if(url.length > 4) { 46 | val bankId = url(2) 47 | val accountId = url(4) 48 | val viewIds = for { 49 | vData <- viewData 50 | if(vData.allowed) 51 | vId <- vData.view.id 52 | } yield vId 53 | 54 | val result = ObpAPI.addPermissions(bankId, accountId, username, viewIds) 55 | result match { 56 | case Failure(msg, _, _) => showMsg(msg) 57 | case _ => { 58 | //Redirect to permissions overview 59 | //TODO: Would be nice to calculate this but at the moment there is nothing to do it 60 | S.redirectTo("/banks/" + bankId + "/accounts/" + accountId + "/permissions") 61 | Noop 62 | } 63 | } 64 | 65 | } else { 66 | logger.warn("Couldn't determine bank and account from url:" + url.toList.toString) 67 | showMsg("error") 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | ".view_row *" #> viewData.map(vData => { 75 | val onOffSwitch = "onoffswitch-view-" + vData.view.id.getOrElse("") 76 | ".view_name *" #> vData.view.short_name.getOrElse(vData.view.id.getOrElse("")) & 77 | ".view_check" #> SHtml.checkbox( 78 | allowed(vData.view), 79 | allowed.put(vData.view, _), 80 | "class" -> "onoffswitch-checkbox view_check", 81 | "id" -> onOffSwitch) & 82 | ".onoffswitch-label [for]" #> onOffSwitch 83 | }) & 84 | "name=email" #> SHtml.text(username, username = _) & 85 | "type=submit" #> SHtml.ajaxSubmit("Grant access", process) 86 | 87 | } 88 | 89 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson) 90 | } 91 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/CustomEditable.scala: -------------------------------------------------------------------------------- 1 | /** 2 | Open Bank Project - Sofi Web Application 3 | Copyright (C) 2011 - 2021, TESOBE GmbH 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | 18 | Email: contact@tesobe.com 19 | TESOBE GmbH 20 | Osloer Str. 16/17 21 | Berlin 13359, Germany 22 | 23 | This product includes software developed at 24 | TESOBE (http://www.tesobe.com/) 25 | by 26 | Simon Redfern : simon AT tesobe DOT com 27 | Stefan Bethge : stefan AT tesobe DOT com 28 | Everett Sochowski : everett AT tesobe DOT com 29 | Ayoub Benali: ayoub AT tesobe DOT com 30 | Nina Gänsdorfer: nina AT tesobe.com 31 | 32 | */ 33 | 34 | package code.snippet 35 | 36 | import net.liftweb.http.SHtml 37 | import scala.xml.NodeSeq 38 | 39 | object CustomEditable { 40 | 41 | import net.liftweb.http.js 42 | import net.liftweb.util.Helpers 43 | import js.{ jquery, JsCmd, JsCmds, JE } 44 | import jquery.JqJsCmds 45 | import JsCmds.{ Noop, SetHtml } 46 | import JE.Str 47 | import JqJsCmds.{ Hide, Show } 48 | 49 | 50 | val addClass = "add" 51 | val editClass = "edit" 52 | val removeClass = "remove" 53 | val noAliasTooltip = "No alias is set, so the real account name will be displayed." 54 | val confirmRemoval = "If no alias is set, the real account name will be displayed." 55 | 56 | def dispName(divName: String) : String = divName + "_display" 57 | def editName(divName: String) : String = divName + "_edit" 58 | 59 | 60 | /* 61 | * Depending on what was clicked only either the "_edit" or the "_display" div is visible 62 | */ 63 | def swapJsCmd(show: String, hide: String): JsCmd = Show(show) & Hide(hide) 64 | def setAndSwap(show: String, showContents: => NodeSeq, hide: String): JsCmd = 65 | (SHtml.ajaxCall(Str("ignore"), { ignore: String => SetHtml(show, showContents) })._2.cmd & swapJsCmd(show, hide)) 66 | 67 | 68 | /* 69 | * The edit markup consists of the input field (editForm), a submit button and a cancel button 70 | * Clicking on either of buttons will swap back to the display markup, the submit button will save the data of the input field 71 | */ 72 | def editMarkup(label : => String, editForm: => NodeSeq, onSubmit: () => JsCmd, onDelete: () => Unit, defaultValue: String, divName: String, removable: Boolean): NodeSeq = { 73 | 74 | val formData: NodeSeq = 75 | editForm ++
76 | ++ 77 | SHtml.hidden(onSubmit, ("float", "left")) ++ 78 | 79 | 80 | SHtml.ajaxForm(formData, 81 | Noop, 82 | setAndSwap(dispName(divName), displayMarkup(label, editForm, onSubmit, onDelete, defaultValue, divName, removable), editName(divName))) 83 | } 84 | 85 | 86 | /* 87 | * The display markup shows the label (default value if none is set), an edit button and for some fields (currently only the alias fields) also a remove button 88 | * Clicking on the edit button will swap to the edit markup 89 | * Clicking on the remove button will pop up a confirmation window 90 | */ 91 | def displayMarkup(label : => String, editForm: => NodeSeq, onSubmit: () => JsCmd, onDelete: () => Unit, defaultValue: String, divName: String, removable: Boolean): NodeSeq = { 92 | 93 | label match { 94 | case "" => { 95 | if(removable){ 96 |
97 |

{ defaultValue }

98 | 99 | 100 |
101 | } else{ 102 |
103 | 104 |

{ " " ++ defaultValue }

105 |
106 | } 107 | } 108 | case _ => { 109 |
110 |

{ label }

111 | 112 | { if (removable) 113 | 114 | } 115 |
116 | } 117 | } 118 | } 119 | 120 | 121 | /* 122 | * Pop-up window when removing alias: on approval deletes alias and empties value in input field of the edit markup 123 | */ 124 | 125 | def removeAlias(label: String, editForm: NodeSeq, onSubmit: () => JsCmd, onDelete: () => Unit, defaultValue: String, divName: String, removable: Boolean): String = { 126 | 127 | def removalConfirmed: JsCmd = { 128 | SHtml.ajaxInvoke(() => { 129 | onDelete() 130 | var empty = "" 131 | setAndSwap(dispName(divName), displayMarkup(empty, SHtml.text(empty, empty = _), onSubmit, onDelete, defaultValue, divName, removable), editName(divName)) 132 | })._2.cmd 133 | } 134 | 135 | val confirmationPopup = JsCmds.Confirm(confirmRemoval, removalConfirmed) 136 | confirmationPopup 137 | } 138 | 139 | /* 140 | * 141 | */ 142 | 143 | def editable(label : => String, editForm: => NodeSeq, onSubmit: () => JsCmd, onDelete: () => Unit, defaultValue: String, removable: Boolean): NodeSeq ={ 144 | val divName = Helpers.nextFuncName 145 | 146 |
147 |
148 | { displayMarkup(label, editForm, onSubmit, onDelete, defaultValue, divName, removable) } 149 |
150 | 153 |
154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/Dashboard.scala: -------------------------------------------------------------------------------- 1 | /** 2 | Open Bank Project - Sofi Web Application 3 | Copyright (C) 2011 - 2021, TESOBE GmbH 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | 18 | Email: contact@tesobe.com 19 | TESOBE GmbH. 20 | Osloer Str. 16/17 21 | Berlin 13359, Germany 22 | 23 | This product includes software developed at 24 | TESOBE (http://www.tesobe.com/) 25 | by 26 | Simon Redfern : simon AT tesobe DOT com 27 | Stefan Bethge : stefan AT tesobe DOT com 28 | Everett Sochowski : everett AT tesobe DOT com 29 | Ayoub Benali: ayoub AT tesobe DOT com 30 | 31 | */ 32 | package code.snippet 33 | 34 | import code.lib.{OAuthClient, ObpAPI} 35 | import code.lib.ObpJson._ 36 | import code.util.Helper.MdcLoggable 37 | import net.liftweb.common.Full 38 | 39 | class OBPDashboardLeftSnippet (params : (TransactionsJson, AccountJson, TransactionsListURLParams, TransactionsJson, AccountJson, TransactionsListURLParams)) extends OBPTransactionSnippet ((params._1, params._2, params._3)) { 40 | } 41 | 42 | class OBPDashboardRightSnippet (params : (TransactionsJson, AccountJson, TransactionsListURLParams, TransactionsJson, AccountJson, TransactionsListURLParams)) extends OBPTransactionSnippet ((params._4, params._5, params._6)) { 43 | } 44 | 45 | 46 | 47 | /////////////// 48 | 49 | //import code.snippet.CallUrlForm._ 50 | import net.liftweb.http.S 51 | 52 | import scala.xml.NodeSeq 53 | // for compact render 54 | import code.lib.ObpAPI.allBanks 55 | import net.liftweb.http.CurrentReq 56 | import net.liftweb.http.SHtml.ajaxSelect 57 | import net.liftweb.util.Helpers._ 58 | 59 | 60 | /* 61 | Present a list of OBP resource URLs 62 | */ 63 | class OBPDashboard extends MdcLoggable { 64 | 65 | 66 | 67 | val presetBankId = S.param("bank_id").getOrElse("") 68 | logger.info(s"bank_id in url param is $presetBankId") 69 | 70 | val presetAccountId = S.param("account_id").getOrElse("") 71 | logger.info(s"account_id in url param is $presetAccountId") 72 | 73 | val presetViewId = S.param("view_id").getOrElse("") 74 | logger.info(s"account_id in url param is $presetViewId") 75 | 76 | val presetCounterpartyId = S.param("counterparty_id").getOrElse("") 77 | logger.info(s"counterparty_id in url param is $presetCounterpartyId") 78 | 79 | val presetTransactionId = S.param("transaction_id").getOrElse("") 80 | logger.info(s"transaction_id in url param is $presetTransactionId") 81 | 82 | 83 | def stringToNodeSeq(html : String) : NodeSeq = { 84 | scala.xml.XML.loadString("
" + html + "
") 85 | } 86 | 87 | 88 | def modifiedRequestUrl(url: String, presetBankId: String, presetAccountId: String) = { 89 | // Potentially replace BANK_ID 90 | val url2: String = presetBankId match { 91 | case "" => url 92 | case _ => url.replaceAll("BANK_ID", presetBankId) 93 | } 94 | 95 | // Potentially replace ACCOUNT_ID 96 | val url3: String = presetAccountId match { 97 | case "" => url2 98 | case _ => url2.replaceAll("/ACCOUNT_ID", s"/$presetAccountId") // so we don't change OTHER_ACCOUNT_ID 99 | } 100 | 101 | // Potentially replace VIEW_ID 102 | val url4: String = presetViewId match { 103 | case "" => url3 104 | case _ => url3.replaceAll("VIEW_ID", presetViewId) 105 | } 106 | 107 | // Potentially replace OTHER_ACCOUNT_ID 108 | val url5: String = presetCounterpartyId match { 109 | case "" => url4 110 | case _ => url4.replaceAll("OTHER_ACCOUNT_ID", presetCounterpartyId) 111 | } 112 | 113 | // Potentially replace TRANSACTION_ID 114 | val url6: String = presetTransactionId match { 115 | case "" => url5 116 | case _ => url5.replaceAll("TRANSACTION_ID", presetTransactionId) 117 | } 118 | 119 | url6 120 | } 121 | 122 | def showAccountSelector = { 123 | 124 | 125 | val uri = CurrentReq.value.uri 126 | 127 | val banks = allBanks 128 | 129 | // TODO dehardcode the redirect path. 130 | 131 | 132 | def onAccountChange (v: Any) = { 133 | logger.info("account changed to " + v.toString) 134 | S.redirectTo(s"$uri?bank_id=${presetBankId}&account_id=${v}") 135 | } 136 | 137 | 138 | 139 | // Get a list of tuples List(("bank short name", "id"),("bank two", "id2")) to populate the drop down select list. 140 | // Could we write this in a way such that if there are no banks the doBankSelect is not run? 141 | val bankOptions = ("", "Select Bank") :: banks.map(b => b.bankJsons.map(bj => (bj.id.getOrElse(""), bj.short_name.getOrElse("") + " (" + bj.id.getOrElse("") + ")"))).getOrElse(List(("", "No Banks"))) 142 | 143 | // TODO create BankId case class like in the API 144 | type BankID = String 145 | 146 | val privateAccountJsons : List[(String, String)] = for { 147 | privateAccountsJson <- ObpAPI.allAccountsAtOneBank(presetBankId).toList 148 | barebonesAccountJson <- privateAccountsJson.accounts.toList.flatten 149 | //bankId <- barebonesAccountJson.bank_id 150 | accountId <- barebonesAccountJson.id 151 | label <- barebonesAccountJson.label 152 | } yield (accountId, label) 153 | 154 | def getAccountOptions : List[(String,String)] = { 155 | 156 | val selectAccount = ("", "Select Account") 157 | val noneFound = ("", "") // No Accounts Found 158 | 159 | val options: List[(String, String)] = presetBankId match { 160 | case "" => List(noneFound) 161 | case _ => for { 162 | allAccountsJson <- ObpAPI.allAccountsAtOneBank(presetBankId).toList 163 | barebonesAccountJson <- allAccountsJson.accounts.toList.flatten 164 | accountId <- barebonesAccountJson.id 165 | label <- barebonesAccountJson.label 166 | } yield (accountId, label) 167 | } 168 | 169 | selectAccount :: options 170 | } 171 | 172 | // def getViewOptions : List[(String,String)] = { 173 | // 174 | // val selectOne = OAuthClient.loggedIn match { 175 | // case true => ("", "Select View") 176 | // case false => ("", "Login for Views") 177 | // } 178 | // 179 | // val noneFound = ("", "") // No Views Found 180 | // 181 | // // TODO Should check for both presetBankId and presetAccountId 182 | // // Logged in user required? 183 | // val options: List[(String, String)] = presetAccountId match { 184 | // case "" => List(noneFound) 185 | // case _ => for { 186 | // views <- ObpAPI.getViewsForBankAccount(presetBankId, presetAccountId).toList 187 | // view <- views.views.toList.flatten 188 | // viewId <- view.id 189 | // shortName <- view.short_name 190 | // } yield (viewId, shortName) 191 | // } 192 | // 193 | // selectOne :: options 194 | // } 195 | 196 | 197 | 198 | // Drop down box to select account. Selected item taken from url param. 199 | def doAccountSelect(in: NodeSeq) = ajaxSelect(getAccountOptions, 200 | Full(presetAccountId), 201 | v => onAccountChange(v)) 202 | 203 | // Drop down box to select view for bank/account. Selected item taken from url param. 204 | // def doViewSelect(in: NodeSeq) = ajaxSelect(getViewOptions, 205 | // Full(presetViewId), 206 | // v => onViewChange(v)) 207 | 208 | 209 | 210 | 211 | def loggedInStatusMessage = { 212 | if (OAuthClient.loggedIn) "" else "Some options and calls require login." 213 | } 214 | 215 | 216 | ".account_selector" #> doAccountSelect _ 217 | } 218 | 219 | } 220 | 221 | 222 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/Login.scala: -------------------------------------------------------------------------------- 1 | /** 2 | Open Bank Project - Sofi Web Application 3 | Copyright (C) 2011 - 2021, TESOBE GmbH 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | 18 | Email: contact@tesobe.com 19 | TESOBE GmbH. 20 | Osloer Str. 16/17 21 | Berlin 13359, Germany 22 | 23 | This product includes software developed at 24 | TESOBE (http://www.tesobe.com/) 25 | by 26 | Simon Redfern : simon AT tesobe DOT com 27 | Stefan Bethge : stefan AT tesobe DOT com 28 | Everett Sochowski : everett AT tesobe DOT com 29 | Ayoub Benali: ayoub AT tesobe DOT com 30 | 31 | */ 32 | 33 | package code.snippet 34 | 35 | import code.Constant 36 | import code.lib.{OAuthClient, ObpAPI} 37 | import code.util.Helper.MdcLoggable 38 | import code.util.{Helper, Util} 39 | import net.liftweb.common.{Box, Full} 40 | import net.liftweb.http.js.JsCmd 41 | import net.liftweb.http.js.JsCmds.Noop 42 | import net.liftweb.http.provider.HTTPCookie 43 | import net.liftweb.http.{S, SHtml} 44 | import net.liftweb.util.Helpers._ 45 | 46 | class Login extends MdcLoggable { 47 | 48 | 49 | private def loggedIn = { 50 | // Correlated User ID Flow 51 | S.cookieValue(Constant.correlatedUserIdTargetCookieName) match { 52 | case Full(correlatedUserId) if correlatedUserId != null => 53 | // This will do the full correlatedUserFlow 54 | logger.debug("Expecting full correlatedUserFlow") 55 | Util.correlatedUserFlow(Some(correlatedUserId)) 56 | case _ => 57 | // This will do the partial correlatedUserFlow i.e. maybe add a customer and user customer link 58 | logger.debug("Expecting partial correlatedUserFlow") 59 | Util.correlatedUserFlow(None) 60 | } 61 | def getDisplayNameOfUser(): Box[String] = { 62 | ObpAPI.currentUser.map { 63 | u => 64 | u.provider.toLowerCase() match { 65 | case provider if provider.contains("google") => u.email 66 | case provider if provider.contains("yahoo") => u.email 67 | case _ => u.username 68 | } 69 | } 70 | } 71 | ".logged-out *" #> "" & 72 | "#logged-in-name *" #> getDisplayNameOfUser() & 73 | "#logout [onclick+]" #> SHtml.onEvent(s => { 74 | OAuthClient.logoutAll() 75 | Noop 76 | }) 77 | } 78 | 79 | def loggedOut = { 80 | ".logged-in *" #> "" & 81 | "#start-login [onclick]" #> { 82 | def actionJS: JsCmd = { 83 | OAuthClient.redirectToOauthLogin() 84 | Noop 85 | } 86 | SHtml.onEvent((s: String) => actionJS) 87 | } 88 | } 89 | 90 | def login = { 91 | if(OAuthClient.loggedIn) loggedIn 92 | else loggedOut 93 | } 94 | 95 | 96 | 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/PermissionManagement.scala: -------------------------------------------------------------------------------- 1 | package code.snippet 2 | 3 | import code.lib.ObpAPI 4 | import code.lib.ObpJson._ 5 | import code.util.Helper.{MdcLoggable, _} 6 | import net.liftweb.common.{Failure, Full} 7 | import net.liftweb.http.{S, SHtml} 8 | import net.liftweb.http.js.JE.JsRaw 9 | import net.liftweb.http.js.JsCmds.{Noop, Script} 10 | import net.liftweb.json._ 11 | import net.liftweb.util.{CssSel, Props} 12 | import net.liftweb.util.Helpers._ 13 | 14 | case class PermissionsUrlParams(bankId : String, accountId: String) 15 | case class ClickJson(userId: String, checked: Boolean, viewId : String) 16 | 17 | class PermissionManagement(params : (PermissionsJson, AccountJson, List[ViewJson], PermissionsUrlParams)) extends MdcLoggable { 18 | 19 | val permissionsJson = params._1 20 | val accountJson = params._2 21 | val nonPublicViews : List[ViewJson] = params._3.filterNot(_.is_public.getOrElse(true)) 22 | val urlParams = params._4 23 | val NOOP_SELECTOR = "#i_am_an_id_that_should_never_exist" #> "" 24 | 25 | implicit val formats = DefaultFormats 26 | 27 | def rowId(userId: String) = "permission_row_" + userId 28 | 29 | val clickAjax = SHtml.ajaxCall(JsRaw("permissionsCheckBoxCallback(this)"), checkBoxClick) 30 | val removeAjax = SHtml.ajaxCall(JsRaw("this.getAttribute('data-userid')"), userId => { 31 | ObpAPI.removeAllPermissions(urlParams.bankId, urlParams.accountId, userId) 32 | Noop 33 | }) 34 | 35 | def checkBoxClick(rawData : String) = { 36 | val data = tryo{parse(rawData).extract[ClickJson]} 37 | 38 | data match { 39 | case Full(d) => { 40 | if(d.checked) ObpAPI.addPermission(urlParams.bankId, urlParams.accountId, d.userId, d.viewId) 41 | else ObpAPI.removePermission(urlParams.bankId, urlParams.accountId, d.userId, d.viewId) 42 | } 43 | case Failure(msg, _, _) => logger.warn("Could not parse raw checkbox click data: " + rawData + ", " + msg) 44 | case _ => logger.warn("Could not parse raw checkbox click data: " + rawData) 45 | } 46 | 47 | Noop 48 | } 49 | 50 | val checkBoxJsFunc = JsRaw(""" 51 | function permissionsCheckBoxCallback(checkbox) { 52 | var json = { 53 | "userId" : checkbox.getAttribute("data-userid"), 54 | "checked" : checkbox.checked, 55 | "viewId" : checkbox.getAttribute("data-viewid") 56 | } 57 | return JSON.stringify(json); 58 | } 59 | """).cmd 60 | 61 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson) 62 | 63 | private def getAllowedSystemVies = { 64 | val allowedSystemViews: List[String] = Props.get("sytems_views_to_display", "owner,accountant,auditor") 65 | .split(",").map(_.trim()).toList 66 | allowedSystemViews 67 | } 68 | 69 | def accountViewHeaders = { 70 | val viewNames : List[String] = nonPublicViews.filter(i => getAllowedSystemVies.exists(i.id.getOrElse("").toLowerCase == _.toLowerCase) || i.id.getOrElse("").startsWith("_")).map(_.short_name.getOrElse("")) 71 | 72 | ".view_name *" #> viewNames 73 | } 74 | 75 | def checkBoxes(permission : PermissionJson) = { 76 | ".view-checkbox *" #> nonPublicViews.filter(i => getAllowedSystemVies.exists(i.id.getOrElse("").toLowerCase == _.toLowerCase) || i.id.getOrElse("").startsWith("_")).map(view => { 77 | 78 | val permissionExists = (for { 79 | views <- permission.views 80 | }yield { 81 | views.exists(_.id == (view.id)) 82 | }).getOrElse(false) 83 | 84 | val checkedSelector : CssSel = 85 | if(permissionExists) {{".check [checked]"} #> "checked"} 86 | else NOOP_SELECTOR 87 | 88 | val userid = permission.user.flatMap(_.id).getOrElse("invalid_userid") 89 | val onOffSwitch = "onoffswitch-user-" + userid + "-" + view.id.getOrElse("invalid_view_id") 90 | checkedSelector & 91 | ".check [onclick]" #> clickAjax & 92 | ".check [data-userid]" #> userid & 93 | ".check [data-viewid]" #> view.id & 94 | ".check [name]" #> onOffSwitch & 95 | ".check [id]" #> onOffSwitch & 96 | ".onoffswitch-label [for]" #> onOffSwitch 97 | }) 98 | } 99 | 100 | def manage = { 101 | 102 | permissionsJson.permissions match { 103 | case None => "* *" #> "No permissions exist" 104 | case Some(ps) => { 105 | ".callback-script" #> Script(checkBoxJsFunc) & 106 | ".row" #> { 107 | ps.map(permission => { 108 | val userId = permission.user.flatMap(_.id).getOrElse("") 109 | 110 | "* [id]" #> rowId(userId) & 111 | ".user *" #> permission.user.flatMap(_.id).getOrElse("") & 112 | checkBoxes(permission) & 113 | ".remove [data-userid]" #> userId & 114 | ".remove [onclick]" #> removeAjax 115 | }) 116 | } 117 | } 118 | } 119 | 120 | } 121 | 122 | def addPermissionLink = { 123 | //TODO: Should generate this url instead of hardcode it 124 | "* [href]" #> {S.uri + "/create"} 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/scala/code/snippet/TimeHelpers.scala: -------------------------------------------------------------------------------- 1 | package code.snippet 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.Calendar 5 | 6 | import net.liftweb.util.Helpers._ 7 | 8 | /** 9 | * So we can put the current year in a template 10 | */ 11 | 12 | class TimeHelpers { 13 | 14 | val now = Calendar.getInstance().getTime() 15 | // create the date/time formatter 16 | val yearFormat = new SimpleDateFormat("yyyy") 17 | val nowYear = yearFormat.format(now) 18 | 19 | def currentYear = "#current_year" #> nowYear.toString 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/code/util/ExceptionLogger.scala: -------------------------------------------------------------------------------- 1 | package code.util 2 | 3 | import net.liftweb.common.{Failure, Full, Box} 4 | import code.util.Helper.MdcLoggable 5 | import net.liftweb.util.{Mailer, Props} 6 | 7 | object MyExceptionLogger extends MdcLoggable{ 8 | import net.liftweb.http.Req 9 | 10 | def unapply(in: (Props.RunModes.Value, Req, Throwable)): Option[(Props.RunModes.Value, Req, Throwable)] = { 11 | import net.liftweb.util.Helpers.now 12 | import Mailer.{From, To, Subject, PlainMailBodyType} 13 | 14 | val outputStream = new java.io.ByteArrayOutputStream 15 | val printStream = new java.io.PrintStream(outputStream) 16 | in._3.printStackTrace(printStream) 17 | val currentTime = now.toString 18 | val stackTrace = new String(outputStream.toByteArray) 19 | val error = currentTime + ": " + stackTrace 20 | val host = Props.get("base_url", "unknown host") 21 | 22 | val mailSent = for { 23 | from <- Props.get("mail.exception.sender.address") ?~ "Could not send mail: Missing props param for 'from'" 24 | // no spaces, comma separated e.g. mail.api.consumer.registered.notification.addresses=notify@example.com,notify2@example.com,notify3@example.com 25 | toAddressesString <- Props.get("mail.exception.registered.notification.addresses") ?~ "Could not send mail: Missing props param for 'to'" 26 | } yield { 27 | 28 | //technically doesn't work for all valid email addresses so this will mess up if someone tries to send emails to "foo,bar"@example.com 29 | val to = toAddressesString.split(",").toList 30 | val toParams = to.map(To(_)) 31 | val params = PlainMailBodyType(error) :: toParams 32 | 33 | //this is an async call 34 | Mailer.sendMail( 35 | From(from), 36 | Subject("you got an exception on "+host), 37 | params :_* 38 | ) 39 | } 40 | 41 | //if Mailer.sendMail wasn't called (note: this actually isn't checking if the mail failed to send as that is being done asynchronously) 42 | if(mailSent.isEmpty) 43 | logger.warn("Exception notification failed: " +mailSent) 44 | 45 | None 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/scala/code/util/Helper.scala: -------------------------------------------------------------------------------- 1 | package code.util 2 | 3 | import code.Constant._ 4 | import code.lib.ObpAPI.getAccount 5 | import code.lib.ObpJson.AccountJson 6 | import net.liftweb.common._ 7 | import net.liftweb.util.Props 8 | 9 | 10 | object Helper { 11 | 12 | 13 | 14 | // From bankId / accountId 15 | def getAccountTitle (bankId: String, accountId: String) : String = { 16 | val accountJsonBox = getAccount(bankId, accountId, CUSTOM_OWNER_VIEW_ID) 17 | 18 | val accountTitle = accountJsonBox match { 19 | case Full(accountJson) => getAccountTitle(accountJson) 20 | case _ => "Unknown Account" 21 | } 22 | accountTitle 23 | } 24 | 25 | 26 | /* 27 | Returns a string which can be used for the title of the account 28 | Uses the Label else Id if possible 29 | */ 30 | def getAccountTitle(accountJson: AccountJson ) : String = { 31 | accountJson.label.getOrElse(accountJson.id.getOrElse("---")) 32 | } 33 | 34 | 35 | def hasManagementAccess (accountJson: AccountJson ) : Boolean = { 36 | val availableViews = accountJson.views_available.toList.flatten 37 | availableViews.exists(view => view.id == Some(CUSTOM_OWNER_VIEW_ID)) 38 | } 39 | 40 | 41 | def getHostname(): String = { 42 | Props.get("base_url", "") match { 43 | case s: String if s.nonEmpty => s.split(":").lift(1) match { 44 | case Some(s) => s.replaceAll("\\/", "").replaceAll("\\.", "-") 45 | case None => "unknown" 46 | } 47 | case _ => "unknown" 48 | } 49 | } 50 | 51 | trait MdcLoggable extends Loggable { 52 | MDC.put("host" -> getHostname) 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/scala/code/util/Util.scala: -------------------------------------------------------------------------------- 1 | package code.util 2 | 3 | import java.text.SimpleDateFormat 4 | import java.time.LocalDate 5 | import java.time.format.DateTimeFormatter 6 | import java.util.{Calendar, Date, Locale} 7 | 8 | import code.Constant 9 | import code.lib.ObpAPI 10 | import code.util.Helper.MdcLoggable 11 | import net.liftweb.common.Full 12 | import net.liftweb.http.S 13 | import net.liftweb.http.provider.HTTPCookie 14 | import net.liftweb.util.Props 15 | 16 | import scala.util.Try 17 | 18 | object Util extends MdcLoggable { 19 | def correlatedUserFlow(correlatedUserId: Option[String]): String = { 20 | logger.debug("Hello from the correlatedUserFlow, Correlated User ID: " + correlatedUserId) 21 | ObpAPI.currentUser match { 22 | case Full(currentUser) => 23 | val currentUserId: String = currentUser.user_id 24 | val bankId = "user." + currentUserId 25 | ObpAPI.getOrCreateCustomer(bankId, legalName = currentUser.username) match { 26 | case Full(customerId) => 27 | S.addCookie(HTTPCookie(Constant.correlatedCustomerIdCreatedCookieName, customerId)) 28 | // TODO refactor this so that we check if there are links instead of relying on the error message. 29 | logger.debug(s"Before createUserCustomerLinkIfDoesNotExists bankid: $bankId currentUserId: $currentUserId customerId: $customerId") 30 | val loggedOnUserIdDone = ObpAPI.createUserCustomerLinkIfDoesNotExists(bankId, currentUserId, customerId) 31 | if(loggedOnUserIdDone) S.addCookie(HTTPCookie(Constant.linkBetweenCorrelatedUserAndCustomerCreatedCookieName, currentUserId)) 32 | 33 | correlatedUserId match { 34 | case Some(localCorrelatedUserId) => 35 | logger.debug("Before create user customer link Customer ID: " + customerId + " Correlated User ID: " + correlatedUserId) 36 | val correlatedUserIdDone = ObpAPI.createUserCustomerLinkIfDoesNotExists(bankId, localCorrelatedUserId, customerId) 37 | if(loggedOnUserIdDone) S.addCookie(HTTPCookie(Constant.correlatedUserIdBoundCookieName, localCorrelatedUserId)) // Seems this cookie is only for info purposes 38 | if(loggedOnUserIdDone && correlatedUserIdDone) { 39 | S.deleteCookie(Constant.correlatedUserIdTargetCookieName) 40 | "loggedOnUserIdDone&&correlatedUserIdDone" 41 | } else { 42 | "NOT (loggedOnUserIdDone&&correlatedUserIdDone)" 43 | } 44 | case _ => 45 | logger.debug("No correlated user_id supplied, Not creating correlated user.") 46 | "no correlatedUserId just customer" 47 | } 48 | 49 | case someIssue => 50 | logger.error("Correlated User Flow Error: " + someIssue) 51 | "Error" 52 | } 53 | case _ => 54 | logger.debug("Correlated User Flow - user is not logged in") 55 | "NotLoggedIn" 56 | } 57 | } 58 | 59 | def monthsAgo(months: Int): Date = { 60 | val currentDate = new Date() 61 | val calendar = Calendar.getInstance 62 | calendar.setTime(currentDate) 63 | calendar.add(Calendar.MONTH, -months) 64 | val pastDate: Date = calendar.getTime 65 | pastDate 66 | } 67 | 68 | def getLocalDate(date: Date): String = { 69 | import java.text.DateFormat 70 | val df = DateFormat.getDateInstance(DateFormat.LONG, currentLocale()) 71 | val formattedDate = df.format(date) 72 | formattedDate 73 | } 74 | 75 | def getLocale(): Locale = Locale.getAvailableLocales().toList.filter { l => 76 | l.toLanguageTag == Props.get("language_tag", "en-GB") 77 | }.headOption.getOrElse(Locale.ENGLISH) 78 | 79 | def currentLocale() : Locale = { 80 | val locale = getLocale() 81 | // Cookie name 82 | val localeCookieName = "SELECTED_LOCALE" 83 | S.findCookie(localeCookieName).flatMap { 84 | cookie => cookie.value.map(computeLocale) 85 | } openOr locale 86 | } 87 | // Properly convert a language tag to a Locale 88 | def computeLocale(tag : String) = tag.split(Array('-', '_')) match { 89 | case Array(lang) => new Locale(lang) 90 | case Array(lang, country) => new Locale(lang, country) 91 | case Array(lang, country, variant) => new Locale(lang, country, variant) 92 | } 93 | 94 | def getTimeAgo(date: Date): String = { 95 | import com.github.marlonlom.utilities.timeago.TimeAgo 96 | import com.github.marlonlom.utilities.timeago.TimeAgoMessages 97 | val messages = new TimeAgoMessages.Builder().withLocale(currentLocale()).build 98 | val timeInMillis = date.getTime() 99 | val text = TimeAgo.using(timeInMillis, messages) 100 | text 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/scala/code/widgets/CustomTableSorter.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * The difference between this and lift's TableSorter.scala is that this one will use a text extractor function that 3 | * will search a td for an element with class "text" and use that to sort on. This allows for markup in a td rather than 4 | * just plain strings. Requires jquery. If no "text" class is found in the table, it will just use the table cell element's innerHTML to 5 | * sort on. 6 | * 7 | */ 8 | 9 | /* 10 | * Copyright 2007-2010 WorldWide Conferencing, LLC 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | package code.widgets.tableSorter { 26 | 27 | import _root_.net.liftweb.http.ResourceServer 28 | import _root_.scala.xml.NodeSeq 29 | import _root_.net.liftweb.http.{LiftRules} 30 | import _root_.net.liftweb.http.js._ 31 | import JsCmds._ 32 | import JE._ 33 | import jquery.JqJE._ 34 | 35 | // types to ease the creation of a JsObj to configure the TableSorter plugin. 36 | trait TableSorterOption[T] { 37 | 38 | def toJsObj: JsObj 39 | 40 | } 41 | 42 | // Because of the whacky js api we have to deal with sorter options 43 | // being either a string specifying the name of the sorter or the boolean value 44 | // false in the case you want to disable the sorting. 45 | 46 | trait SorterOption[T] extends TableSorterOption[T] { 47 | val sorter: T 48 | } 49 | 50 | /** 51 | * This is used to create the special case of SorterOption where sorting is 52 | * disabled 53 | */ 54 | case class DisableSorting() extends SorterOption[Boolean]{ 55 | 56 | val sorter = false 57 | 58 | def toJsObj = JsObj("sorter" -> sorter) 59 | } 60 | 61 | // Allows the user to type stuff like Sorting.DSC to specify that a column should be sorted 62 | // decening instead of just a plain integer that the js api expects. 63 | object Sorting extends Enumeration { 64 | type Sorting = Value 65 | val ASC, DSC = Value 66 | } 67 | 68 | /** 69 | * Sorter is used to define how the TableSorter should sort the columns. The possible list 70 | * of build in sorters are: shortDate, usLongDate, percent, isoDate, url, ipAddress, currency, 71 | * digit, text, shortDate, time, metadata 72 | * 73 | */ 74 | case class Sorter(sorter: String) extends SorterOption[String] { 75 | def toJsObj = JsObj("sorter" -> sorter) 76 | } 77 | 78 | object CustomTableSorter { 79 | 80 | private val emptyJsObj = new JsObj { 81 | def props = Nil 82 | } 83 | 84 | 85 | def apply(selector: String): NodeSeq = renderOnLoad(selector, CustomTableSorter.options()) 86 | def apply(selector: String, options: String): NodeSeq = renderOnLoad(selector, options) 87 | 88 | /** 89 | * Initializes the widget. You have to call this in boot for the widget to work. 90 | */ 91 | def init() { 92 | ResourceServer.allow({ 93 | case "tablesorter" :: tail => true 94 | }) 95 | } 96 | 97 | import Sorting._ 98 | 99 | /** 100 | * Use this method to create a type-safe configuration JsObj for the 101 | * TableSorter. See http://tablesorter.com/docs/ for more information 102 | * about the possible configurations of the jQuery plugin. 103 | * 104 | * Example usage: TableSorter.options( 105 | * headers = (0, DisableSorting()) :: (3,Sorter("currency")) :: Nil, 106 | * sortList = (3,Sorting.DSC) :: Nil) 107 | * 108 | * @param headers A list of tuples of int * sorter options. the int is the column number. 109 | * The column number is indexed starting from 0 110 | * @param sortList A list of tuples of int * Sorting. the int is the column number. 111 | * Use DisableSorting to disable sorting 112 | * @return A JsObj with valid properties to configure the jQuery TableSorter plugin 113 | */ 114 | def options(headers: List[(Int, TableSorterOption[_])], 115 | widgets: List[String], 116 | sortList: List[(Int, Sorting)]): String = { 117 | 118 | val jsHeaders = headers 119 | .map{ case (index, header) => JsObj(index.toString -> header.toJsObj)} 120 | .foldLeft[JsObj](emptyJsObj)(_ +* _) 121 | val jSSortList = JsArray( sortList.map{ case (index, sorting) => JsArray(index,sorting.id)}:_*) 122 | val widgetsArr = JsArray ( widgets.map( Str(_) ):_* ) 123 | "{" + "sortList:" + jSSortList.toJsCmd + ",headers:" + jsHeaders.toJsCmd + ",widgets:" + widgetsArr.toJsCmd + ",textExtraction:" + 124 | """function(node){var sortOn = $(node).find(".text").first().html(); return sortOn? sortOn : node.innerHTML}""" + "}" 125 | } 126 | 127 | //Convenience versions of the options method that provide default values for all the arguments. 128 | def options(): String = options(Nil, List("zebra"), List((0, ASC))) 129 | 130 | def options(headers: List[(Int, TableSorterOption[_])], 131 | sortList: List[(Int, Sorting)]): String = options(headers, List("zebra"), sortList) 132 | 133 | /** 134 | * Transforms a regular table into a tablesorter when page is loaded 135 | * 136 | * @param selector A CSS selector for the table you want to transform 137 | * @param options A JsObject configuring the tablesorter. You can use the connivance method 138 | * TableSorter.options to create the JsObj in a type-safe manner. Check out 139 | http://tablesorter.com/docs/ for more info about the possible configurations. 140 | */ 141 | def renderOnLoad(selector: String, options: String): NodeSeq = { 142 | val onLoad = """jQuery(document).ready(function(){ 143 | jQuery('"""+selector+"""').tablesorter("""+options+""") 144 | });""" 145 | 146 | 147 | 41 | 42 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/webapp/banks/star/accounts/star/create-income.html: -------------------------------------------------------------------------------- 1 | 29 | 30 | 34 | 35 |
36 | 41 | 42 | 81 | 82 |
83 | -------------------------------------------------------------------------------- /src/main/webapp/banks/star/accounts/star/management.html: -------------------------------------------------------------------------------- 1 | 31 |
32 | 64 | 65 |
66 | -------------------------------------------------------------------------------- /src/main/webapp/banks/star/accounts/star/permissions.html: -------------------------------------------------------------------------------- 1 | 31 |
32 | 33 | 75 | 76 |
77 | -------------------------------------------------------------------------------- /src/main/webapp/banks/star/accounts/star/permissions/create.html: -------------------------------------------------------------------------------- 1 | 31 |
32 | 75 |
76 | -------------------------------------------------------------------------------- /src/main/webapp/banks/star/accounts/star/settings.html: -------------------------------------------------------------------------------- 1 | 29 | 30 | 34 | 35 |
36 | 37 | 38 | 62 | 63 |
64 | -------------------------------------------------------------------------------- /src/main/webapp/banks/star/accounts/star/star.html: -------------------------------------------------------------------------------- 1 | 31 | 32 | 33 | 41 | 42 |
43 | 44 | 45 | 89 | 90 |
91 | -------------------------------------------------------------------------------- /src/main/webapp/banks/star/accounts/star/transactions/star/star.html: -------------------------------------------------------------------------------- 1 | 31 | 32 | 36 | 37 | 38 |
39 | 40 | 122 | 123 |
124 | -------------------------------------------------------------------------------- /src/main/webapp/banks/star/accounts/star/views/list.html: -------------------------------------------------------------------------------- 1 | 31 | 32 | 39 | 40 |
41 | 42 | 117 | 118 |
119 | -------------------------------------------------------------------------------- /src/main/webapp/correlated-user.html: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 | 35 |
-------------------------------------------------------------------------------- /src/main/webapp/dd.html: -------------------------------------------------------------------------------- 1 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 |
36 |

YOUR ACCOUNTS DD

37 | 42 |
43 | 44 | 45 | 46 | 47 |
48 |

Centre for Transparency & Civic Engagement (PARTICIP) AND Fairnopoly

49 |
50 | 51 | 52 | 53 | 54 |
55 | 56 | -------------------------------------------------------------------------------- /src/main/webapp/debug-info.html: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 | 22 | 35 | 36 | 37 | 38 | 39 |
-------------------------------------------------------------------------------- /src/main/webapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/favicon.ico -------------------------------------------------------------------------------- /src/main/webapp/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /src/main/webapp/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/main/webapp/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/main/webapp/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/main/webapp/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/main/webapp/help.html: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 | 30 |
-------------------------------------------------------------------------------- /src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 31 | 32 |
33 | 34 | 35 | 83 | 84 | 85 |
86 | -------------------------------------------------------------------------------- /src/main/webapp/list-accounts.html: -------------------------------------------------------------------------------- 1 | 31 |
32 | 33 | 34 | 35 | 54 |
55 | -------------------------------------------------------------------------------- /src/main/webapp/media/css/highlight.js.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:#f0f0f0;-webkit-text-size-adjust:none}.hljs,.hljs-subst,.hljs-tag .hljs-title,.nginx .hljs-title{color:black}.hljs-string,.hljs-title,.hljs-constant,.hljs-parent,.hljs-tag .hljs-value,.hljs-rule .hljs-value,.hljs-preprocessor,.hljs-pragma,.hljs-name,.haml .hljs-symbol,.ruby .hljs-symbol,.ruby .hljs-symbol .hljs-string,.hljs-template_tag,.django .hljs-variable,.smalltalk .hljs-class,.hljs-addition,.hljs-flow,.hljs-stream,.bash .hljs-variable,.pf .hljs-variable,.apache .hljs-tag,.apache .hljs-cbracket,.tex .hljs-command,.tex .hljs-special,.erlang_repl .hljs-function_or_atom,.asciidoc .hljs-header,.markdown .hljs-header,.coffeescript .hljs-attribute,.tp .hljs-variable{color:#800}.smartquote,.hljs-comment,.hljs-annotation,.diff .hljs-header,.hljs-chunk,.asciidoc .hljs-blockquote,.markdown .hljs-blockquote{color:#888}.hljs-number,.hljs-date,.hljs-regexp,.hljs-literal,.hljs-hexcolor,.smalltalk .hljs-symbol,.smalltalk .hljs-char,.go .hljs-constant,.hljs-change,.lasso .hljs-variable,.makefile .hljs-variable,.asciidoc .hljs-bullet,.markdown .hljs-bullet,.asciidoc .hljs-link_url,.markdown .hljs-link_url{color:#080}.hljs-label,.ruby .hljs-string,.hljs-decorator,.hljs-filter .hljs-argument,.hljs-localvars,.hljs-array,.hljs-attr_selector,.hljs-important,.hljs-pseudo,.hljs-pi,.haml .hljs-bullet,.hljs-doctype,.hljs-deletion,.hljs-envvar,.hljs-shebang,.apache .hljs-sqbracket,.nginx .hljs-built_in,.tex .hljs-formula,.erlang_repl .hljs-reserved,.hljs-prompt,.asciidoc .hljs-link_label,.markdown .hljs-link_label,.vhdl .hljs-attribute,.clojure .hljs-attribute,.asciidoc .hljs-attribute,.lasso .hljs-attribute,.coffeescript .hljs-property,.hljs-phony{color:#88f}.hljs-keyword,.hljs-id,.hljs-title,.hljs-built_in,.css .hljs-tag,.hljs-doctag,.smalltalk .hljs-class,.hljs-winutils,.bash .hljs-variable,.pf .hljs-variable,.apache .hljs-tag,.hljs-type,.hljs-typename,.tex .hljs-command,.asciidoc .hljs-strong,.markdown .hljs-strong,.hljs-request,.hljs-status,.tp .hljs-data,.tp .hljs-io{font-weight:bold}.asciidoc .hljs-emphasis,.markdown .hljs-emphasis,.tp .hljs-units{font-style:italic}.nginx .hljs-built_in{font-weight:normal}.coffeescript .javascript,.javascript .xml,.lasso .markup,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:0.5} -------------------------------------------------------------------------------- /src/main/webapp/media/css/jquery.dropdown.min.css: -------------------------------------------------------------------------------- 1 | .jq-dropdown{position:absolute;z-index:1039;display:none}.jq-dropdown .jq-dropdown-menu,.jq-dropdown .jq-dropdown-panel{min-width:160px;max-width:360px;list-style:none;background:#fff;border:solid 1px #ddd;border-radius:4px;box-shadow:0 5px 10px rgba(0,0,0,0.2);overflow:visible;padding:4px 0;margin:0}.jq-dropdown .jq-dropdown-panel{padding:10px}.jq-dropdown.jq-dropdown-tip{margin-top:8px}.jq-dropdown.jq-dropdown-tip:before{position:absolute;top:-6px;left:9px;content:"";border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ddd;display:inline-block}.jq-dropdown.jq-dropdown-tip:after{position:absolute;top:-5px;left:10px;content:"";border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;display:inline-block}.jq-dropdown.jq-dropdown-tip.jq-dropdown-anchor-right:before{left:auto;right:9px}.jq-dropdown.jq-dropdown-tip.jq-dropdown-anchor-right:after{left:auto;right:10px}.jq-dropdown.jq-dropdown-scroll .jq-dropdown-menu,.jq-dropdown.jq-dropdown-scroll .jq-dropdown-panel{max-height:180px;overflow:auto}.jq-dropdown .jq-dropdown-menu li{list-style:none;padding:0 0;margin:0;line-height:18px}.jq-dropdown .jq-dropdown-menu li>a,.jq-dropdown .jq-dropdown-menu label{display:block;color:inherit;text-decoration:none;line-height:18px;padding:3px 15px;margin:0;white-space:nowrap}.jq-dropdown .jq-dropdown-menu li>a:hover,.jq-dropdown .jq-dropdown-menu label:hover{background-color:#f2f2f2;color:inherit;cursor:pointer}.jq-dropdown .jq-dropdown-menu .jq-dropdown-divider{font-size:1px;border-top:solid 1px #e5e5e5;padding:0;margin:5px 0} 2 | -------------------------------------------------------------------------------- /src/main/webapp/media/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /src/main/webapp/media/css/screen.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/css/screen.css -------------------------------------------------------------------------------- /src/main/webapp/media/css/toastr.min.css: -------------------------------------------------------------------------------- 1 | .toast-title{font-weight:bold}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:bold;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80)}.toast-close-button:hover,.toast-close-button:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px 3px 3px 3px;-webkit-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80)}#toast-container>:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=")!important}#toast-container>.toast-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=")!important}#toast-container>.toast-success{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==")!important}#toast-container>.toast-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=")!important}#toast-container.toast-top-full-width>div,#toast-container.toast-bottom-full-width>div{width:96%;margin:auto}.toast{background-color:#030303}.toast-success{background-color:#51a351}.toast-error{background-color:#bd362f}.toast-info{background-color:#2f96b4}.toast-warning{background-color:#f89406}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:241px)and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:481px)and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}} -------------------------------------------------------------------------------- /src/main/webapp/media/images/OBP_full_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/OBP_full_web.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/OBP_logo_simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/OBP_logo_simple.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/OpenBankProject_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/OpenBankProject_logo.jpg -------------------------------------------------------------------------------- /src/main/webapp/media/images/add-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/add-off.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/add-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/add-on.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/ajax-loader.gif -------------------------------------------------------------------------------- /src/main/webapp/media/images/arrow-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/arrow-right.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/blank.gif -------------------------------------------------------------------------------- /src/main/webapp/media/images/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/cancel.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/close-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/close-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/comment.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/edit-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/edit-off.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/edit-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/edit-on.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/favicon.ico -------------------------------------------------------------------------------- /src/main/webapp/media/images/feedback-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/feedback-button.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/header.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/home-table-enter-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/home-table-enter-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/home-table-exit-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/home-table-exit-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/home-table-paper-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/home-table-paper-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/home-table-quote-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/home-table-quote-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/login-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/login-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/logo-footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/logo-footer.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/logo-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/logo-header.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/logo.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/logout-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/logout-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/money-in-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/money-in-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/money-out-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/money-out-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/moreInfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/moreInfo.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/nav-item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/nav-item.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/nav-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/nav-selected.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/nav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/nav.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/polarize-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/polarize-logo.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/remove.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/select-arrow-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/select-arrow-home.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/select-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/select-arrow.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/settings-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/settings-icon.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/sort.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/submit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/submit.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/transaction-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/transaction-in.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/transaction-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/transaction-out.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/upload-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/upload-off.png -------------------------------------------------------------------------------- /src/main/webapp/media/images/upload-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/upload-on.png -------------------------------------------------------------------------------- /src/main/webapp/media/js/notifications.js: -------------------------------------------------------------------------------- 1 | var socialFinanceNotifications = { 2 | notify : function(msg) { 3 | toastr.info(msg); 4 | }, 5 | 6 | notifyError : function(msg) { 7 | toastr.error(msg); 8 | }, 9 | 10 | notifyReload : function(msg, delay) { 11 | toastr.info(msg); 12 | if (!delay) var delay = 1500; 13 | setTimeout(function() { 14 | window.location.reload(); 15 | }, delay); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/webapp/media/js/scripts.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 'use strict'; 3 | 4 | var project = {}; 5 | 6 | project.init = function() { 7 | project.counterpartiesFilter(); 8 | project.removeUserFromView(); 9 | project.managementTableSorter(); 10 | // project.transloadit(); 11 | project.fileUpload(); 12 | }; 13 | 14 | project.counterpartiesFilter = function() { 15 | $('.counterparties-table-head__cell').on('click', function() { 16 | $('.counterparties-table-head__cell').removeClass('counterparties-table-head__cell--active'); 17 | $(this).addClass('counterparties-table-head__cell--active'); 18 | }); 19 | }; 20 | 21 | project.removeUserFromView = function() { 22 | $('.users-table-body__cell .remove').click(function () { 23 | $(this).closest('.row').css('background-color', '#CC0000').fadeOut(1000, function() {$(this).remove();}); 24 | }); 25 | }; 26 | 27 | project.managementTableSorter = function() { 28 | $("#management .tablesorter").tablesorter(); 29 | } 30 | 31 | project.transloadit = function() { 32 | $('#imageUploader').transloadit({ 33 | wait: true 34 | }); 35 | } 36 | 37 | project.fileUpload = function() { 38 | $(".file-upload").change(function() { 39 | $(this).siblings('.file-upload-filename').html(this.value); 40 | }); 41 | } 42 | 43 | $(document).ready(project.init); 44 | 45 | })(jQuery); 46 | 47 | -------------------------------------------------------------------------------- /src/main/webapp/media/js/toastr.min.js: -------------------------------------------------------------------------------- 1 | (function(n){n(["jquery"],function(n){return function(){function l(n,t,f){return u({type:r.error,iconClass:i().iconClasses.error,message:n,optionsOverride:f,title:t})}function a(n,t,f){return u({type:r.info,iconClass:i().iconClasses.info,message:n,optionsOverride:f,title:t})}function v(n){e=n}function y(n,t,f){return u({type:r.success,iconClass:i().iconClasses.success,message:n,optionsOverride:f,title:t})}function p(n,t,f){return u({type:r.warning,iconClass:i().iconClasses.warning,message:n,optionsOverride:f,title:t})}function w(r){var u=i();if(t||f(u),r&&n(":focus",r).length===0){r[u.hideMethod]({duration:u.hideDuration,easing:u.hideEasing,complete:function(){c(r)}});return}t.children().length&&t[u.hideMethod]({duration:u.hideDuration,easing:u.hideEasing,complete:function(){t.remove()}})}function b(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:undefined,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:undefined,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",target:"body",closeHtml:"