summaryrefslogtreecommitdiff
path: root/talerbank
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-04-08 16:52:20 +0200
committerFlorian Dold <florian.dold@gmail.com>2016-04-08 17:00:43 +0200
commit90072481161b3361da0af9a13b51a468764c1987 (patch)
treecbaf72af02b9b1d23a042217d3b1aa677b1c09c4 /talerbank
parent8bf550786257a83d76f6a2864484a47fb4d1b9e9 (diff)
downloadbank-90072481161b3361da0af9a13b51a468764c1987.tar.gz
bank-90072481161b3361da0af9a13b51a468764c1987.tar.bz2
bank-90072481161b3361da0af9a13b51a468764c1987.zip
restructure modules
Diffstat (limited to 'talerbank')
-rw-r--r--talerbank/__init__.py0
-rw-r--r--talerbank/app/__init__.py0
-rw-r--r--talerbank/app/admin.py11
-rw-r--r--talerbank/app/apps.py10
-rw-r--r--talerbank/app/captcha.py162
-rw-r--r--talerbank/app/config.py6
-rw-r--r--talerbank/app/config.py.in6
-rw-r--r--talerbank/app/errors.py135
-rw-r--r--talerbank/app/funds_mgmt.py148
-rw-r--r--talerbank/app/history.py79
-rw-r--r--talerbank/app/lib.py208
-rw-r--r--talerbank/app/management/__init__.py0
-rw-r--r--talerbank/app/management/commands/__init__.py0
-rw-r--r--talerbank/app/management/commands/bank_admin.py59
-rw-r--r--talerbank/app/management/commands/basic_accounts.py26
-rw-r--r--talerbank/app/management/commands/check_db.py46
-rw-r--r--talerbank/app/management/commands/dump_talerdb.py57
-rw-r--r--talerbank/app/management/commands/pre_accounts.py53
-rw-r--r--talerbank/app/management/commands/sample_donations.py62
-rw-r--r--talerbank/app/migrations/0001_initial.py42
-rw-r--r--talerbank/app/migrations/__init__.py0
-rw-r--r--talerbank/app/models.py52
-rw-r--r--talerbank/app/my-static/favicon.ico0
-rw-r--r--talerbank/app/my-static/style.css158
-rw-r--r--talerbank/app/schemas.py73
-rw-r--r--talerbank/app/startup.py58
-rw-r--r--talerbank/app/templates/account_disabled.html48
-rw-r--r--talerbank/app/templates/central_page.html237
-rw-r--r--talerbank/app/templates/error.html85
-rw-r--r--talerbank/app/templates/history.html33
-rw-r--r--talerbank/app/templates/home_page.html75
-rw-r--r--talerbank/app/templates/logout.html51
-rw-r--r--talerbank/app/templates/pin_tan.html63
-rw-r--r--talerbank/app/templates/public_account_details.html51
-rw-r--r--talerbank/app/templates/public_histories_reloaded.html92
-rw-r--r--talerbank/app/templates/register.html72
-rw-r--r--talerbank/app/test.py31
-rw-r--r--talerbank/app/tests.py3
-rw-r--r--talerbank/app/urls.py37
-rw-r--r--talerbank/app/user_mgmt.py147
-rw-r--r--talerbank/app/util.py82
-rw-r--r--talerbank/app/views.py42
-rw-r--r--talerbank/settings.py126
-rw-r--r--talerbank/settings.py.in126
-rw-r--r--talerbank/urls.py25
-rw-r--r--talerbank/wsgi.py16
46 files changed, 2893 insertions, 0 deletions
diff --git a/talerbank/__init__.py b/talerbank/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/talerbank/__init__.py
diff --git a/talerbank/app/__init__.py b/talerbank/app/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/talerbank/app/__init__.py
diff --git a/talerbank/app/admin.py b/talerbank/app/admin.py
new file mode 100644
index 0000000..e5f6359
--- /dev/null
+++ b/talerbank/app/admin.py
@@ -0,0 +1,11 @@
+"""
+this file is in the public domain
+"""
+from django.contrib import admin
+from .models import (BankAccount,
+ History)
+
+admin.site.register(BankAccount)
+admin.site.register(History)
+
+# Register your models here.
diff --git a/talerbank/app/apps.py b/talerbank/app/apps.py
new file mode 100644
index 0000000..1912612
--- /dev/null
+++ b/talerbank/app/apps.py
@@ -0,0 +1,10 @@
+"""
+this file is in the public domain
+"""
+from __future__ import unicode_literals
+
+from django.apps import AppConfig
+
+
+class BankConfig(AppConfig):
+ name = 'Bank'
diff --git a/talerbank/app/captcha.py b/talerbank/app/captcha.py
new file mode 100644
index 0000000..e5909b2
--- /dev/null
+++ b/talerbank/app/captcha.py
@@ -0,0 +1,162 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from .user_mgmt import is_logged
+from .funds_mgmt import (Reserve,
+ create_reserve_at_exchange)
+from .schemas import Schemas
+from .lib import (write_log_file,
+ get_currency,
+ check_db,
+ get_pretty_exchange_details,
+ floatify_amount)
+from simplemathcaptcha.fields import (MathCaptchaField,
+ MathCaptchaWidget)
+from django.http import HttpResponse, JsonResponse
+from django.shortcuts import render
+from django.conf import settings
+from django import forms
+from urllib.parse import (urlunparse,
+ urlparse,
+ unquote)
+import hashlib
+import json
+from .errors import (UserNotLogged,
+ BadAmount,
+ MissingGetParameter,
+ WrongMethod,
+ user_not_logged_handler,
+ BadGetParameter,
+ BadPostValue,
+ BadWireDetails,
+ ExchangeUnknown,
+ NoBankMatch,
+ NoWireMethodMatch,
+ bad_get_parameter_handler,
+ bad_post_value_handler,
+ exchange_unknown_handler,
+ wrong_method_handler)
+
+
+class Pin(forms.Form):
+ pin = MathCaptchaField(
+ widget=MathCaptchaWidget(
+ question_tmpl="<div lang=\"en\">What is %(num1)i %(operator)s %(num2)i ?</div>"))
+
+
+@is_logged
+def pin_tan_question_attempt(request):
+ if request.method != 'GET':
+ raise WrongMethod('GET')
+ for param in ["amount_value",
+ "amount_fraction",
+ "amount_currency",
+ "exchange",
+ "reserve_pub",
+ "wire_details"]:
+ if param not in request.GET:
+ raise MissingGetParameter(param)
+ try:
+ amount = {'value': int(request.GET['amount_value']),
+ 'fraction': int(request.GET['amount_fraction']),
+ 'currency': request.GET['amount_currency']}
+ except ValueError:
+ raise BadGetParameter
+ wiredetails = json.loads(unquote(request.GET['wire_details']))
+ Schemas.validate_wiredetails(wiredetails, Schemas.wiredetails_schema)
+ if "test" not in wiredetails:
+ raise NoWireMethodMatch
+ # always false to run local tests..
+ if urlparse(wiredetails['test']['bank_uri']).netloc != request.META['HTTP_HOST'] and False:
+ raise NoBankMatch
+ request.session['account_number'] = wiredetails['test']['account_number']
+ Schemas.validate_amount(amount, Schemas.amount_schema) # raise BadAmount
+ request.session['amount'] = amount
+ request.session['exchange'] = request.GET['exchange']
+ request.session['reserve_pub'] = request.GET['reserve_pub']
+ return render(request, 'pin_tan.html', {'form': Pin(auto_id=False),
+ 'amount': floatify_amount(amount),
+ 'currency': get_currency(request),
+ 'exchange':
+ get_pretty_exchange_details(request.GET['exchange'])})
+
+
+@is_logged
+def pin_tan_question(request):
+ try:
+ return pin_tan_question_attempt(request)
+ except MissingGetParameter as e:
+ return JsonResponse({'reason': e.par + " missing in query parameters"}, status=400)
+ except (BadAmount, BadGetParameter):
+ return JsonResponse({'reason': "bad amount given"}, status=400)
+ except WrongMethod as e:
+ return JsonResponse({'reason': 'only GET method allowed'}, status=405)
+ except BadWireDetails:
+ return JsonResponse({'reason': "bad wiredetails given"}, status=400)
+ except ValueError:
+ return JsonResponse({'reason': "garbage in query string"}, status=400)
+ except NoWireMethodMatch:
+ return JsonResponse({'reason': "incompatible wire methods"}, status=400)
+ except NoBankMatch:
+ return JsonResponse({'reason': "bank given in wiredetails is not this one"}, status=400)
+
+
+@check_db
+@is_logged
+def pin_tan_verify(request):
+ try:
+ return pin_tan_verify_attempt(request)
+ except WrongMethod as e:
+ return wrong_method_handler(request, e)
+ except UserNotLogged:
+ return user_not_logged_handler(request)
+ except BadPostValue:
+ return bad_post_value_handler(request)
+
+
+def pin_tan_verify_attempt(request):
+ if request.method != 'POST':
+ raise WrongMethod("POST")
+ try:
+ given = request.POST['pin_0']
+ hashed_result = request.POST['pin_1']
+ except Exception: # FIXME narrow the Exception type
+ raise BadPostValue
+ hasher = hashlib.new("sha1")
+ hasher.update(settings.SECRET_KEY.encode('utf-8'))
+ hasher.update(given.encode('utf-8'))
+ hashed_attempt = hasher.hexdigest()
+ if hashed_attempt == hashed_result:
+ for param in ["amount", "exchange", "reserve_pub"]:
+ if param not in request.session:
+ return HttpResponse("Not a withdraw session", status=400)
+ reserve = Reserve(request.session['amount'],
+ request.session['exchange'],
+ request.session['account_number'],
+ request.session['reserve_pub'],
+ 'TEST')
+ success_url = urlunparse([request.scheme,
+ request.META['HTTP_HOST'],
+ "/success.html", '', '', ''])
+ try:
+ return create_reserve_at_exchange(request, success_url, reserve)
+ except ExchangeUnknown:
+ return exchange_unknown_handler(request)
+
+ else:
+ return render(request, 'error.html', {'type': "wrong_pin"}, status=400)
diff --git a/talerbank/app/config.py b/talerbank/app/config.py
new file mode 100644
index 0000000..3cf223f
--- /dev/null
+++ b/talerbank/app/config.py
@@ -0,0 +1,6 @@
+"""
+this file is in the public domain
+"""
+pre_accounts = ['Tor', 'GNUnet', 'Taler', 'FSF']
+expects_donations = ['Tor', 'GNUnet', 'Taler', 'FSF']
+explicit_currency = 'PUDOS'
diff --git a/talerbank/app/config.py.in b/talerbank/app/config.py.in
new file mode 100644
index 0000000..9e61b3b
--- /dev/null
+++ b/talerbank/app/config.py.in
@@ -0,0 +1,6 @@
+"""
+this file is in the public domain
+"""
+pre_accounts = ['Tor', 'GNUnet', 'Taler', 'FSF']
+expects_donations = ['Tor', 'GNUnet', 'Taler', 'FSF']
+explicit_currency = '@currencyfinal@'
diff --git a/talerbank/app/errors.py b/talerbank/app/errors.py
new file mode 100644
index 0000000..09159ca
--- /dev/null
+++ b/talerbank/app/errors.py
@@ -0,0 +1,135 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from django.shortcuts import render
+
+
+class BadAmount(Exception):
+ pass
+
+
+class BadWireDetails(Exception):
+ pass
+
+
+class BadIncomingRequest(Exception):
+ pass
+
+
+class MissingGetParameter(Exception):
+ def __init__(self, par_name):
+ self.par = par_name
+
+
+class BadGetParameter(Exception):
+ pass
+
+
+class NoWireMethodMatch(Exception):
+ pass
+
+
+class BadPostValue(Exception):
+ pass
+
+
+class WrongMethod(Exception):
+ def __init__(self, allowed_method):
+ self.allowed_method = allowed_method
+
+
+class ExchangeUnknown(Exception):
+ pass
+
+
+class UserNotLogged(Exception):
+ pass
+
+
+class SubPathNotFound(Exception):
+ pass
+
+
+class CurrencyMismatch(Exception):
+ pass
+
+
+class NoTalerDatabase(Exception):
+ pass
+
+
+class NoBankAccount(Exception):
+ pass
+
+
+class NoBankMatch(Exception):
+ pass
+
+
+def no_bank_account_handler(request):
+ return internal_error_handler(
+ request,
+ "(The bank itself has no account,"
+ " please run 'taler-bank-manage --preaccounts')")
+
+
+def non_supported_wire_method(request):
+ return render(request, 'error.html', {'type': 'non_supported_method'}, status=400)
+
+
+def non_existent_db_handler(request):
+ return internal_error_handler(request, "(db does not exist)")
+
+
+def internal_error_handler(request, hint=False):
+ return render(request, 'error.html', {'type': 'internal_error', 'hint': hint}, status=500)
+
+
+def user_not_logged_handler(request):
+ return render(request, 'error.html', {'type': 'not_logged'}, status=401)
+
+
+def exchange_unknown_handler(request):
+ return render(request, 'error.html', {'type': 'exchange_unknown'}, status=401)
+
+
+def wrong_method_handler(request, err):
+ return render(request,
+ 'error.html',
+ {'type': 'custom',
+ 'custom_title': "Wrong method",
+ 'custom_message': "Only " + err.allowed_method + " allowed"},
+ status=405)
+
+
+def bad_get_parameter_handler(request):
+ return render(request,
+ 'error.html',
+ {'type': 'custom',
+ 'custom_title': "Bad request",
+ 'custom_message': "Invalid parameter in GET request"},
+ status=405)
+
+
+def bad_post_value_handler(request):
+ return render(request,
+ 'error.html',
+ {'type': 'custom',
+ 'custom_title': "Bad request",
+ 'custom_message': "Bad value in POSTed data"},
+ status=400)
diff --git a/talerbank/app/funds_mgmt.py b/talerbank/app/funds_mgmt.py
new file mode 100644
index 0000000..2b45bc0
--- /dev/null
+++ b/talerbank/app/funds_mgmt.py
@@ -0,0 +1,148 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from .user_mgmt import is_logged
+from .schemas import Schemas
+from .errors import (BadIncomingRequest,
+ CurrencyMismatch,
+ WrongMethod)
+from .lib import (write_log_file,
+ check_exchange_account_no,
+ wire_transfer_in_out)
+from django.views.decorators.csrf import csrf_exempt
+from django.http import (HttpResponse,
+ JsonResponse)
+from .models import BankAccount
+from django.shortcuts import (render,
+ redirect)
+from django.db.utils import OperationalError
+from django import forms
+from django.http import HttpResponseServerError
+from urllib.parse import (urlparse,
+ urljoin)
+import requests
+import time
+import json
+
+
+class DebugForm(forms.Form):
+ reserve_pk = forms.CharField(initial="DVHS4CQRC3ZQPW9PD6A5BCTYS9Z460P261M6M660QS7N16BXP19G")
+ kudos_amount = forms.CharField(initial="3.3 KUDOS")
+ mint_rcv = forms.CharField(initial="http://mint.demo.taler.net/")
+
+
+class Reserve:
+ def __init__(self, amount, exchange, exchange_account, reserve_pub, wire_type):
+ # it raises BadAmount if amount is bad
+ Schemas.validate_amount(amount, Schemas.amount_schema)
+ self.amount = amount
+ self.exchange = exchange
+ self.exchange_account = exchange_account
+ self.reserve_pub = reserve_pub
+ self.wire_type = wire_type
+
+
+class Transaction:
+ def __init__(self, direction, amount, counterpart="Unknown"):
+ self.direction = direction
+ self.amount = amount
+ self.counterpart = counterpart
+
+"""
+The CSRF exempt is due to the fact Django looks for an anti-CSRF token
+in any POST it gets. Since the following function is meant to serve mints,
+and mints don't send any such token, it is necessary to disable it.
+Those tokens are normally hidden fields in Django-generated HTML forms.
+"""
+
+
+@csrf_exempt
+def add_incoming_attempt(request):
+ try:
+ return add_incoming(request)
+ except ValueError:
+ return JsonResponse({'reason': 'malformed json'}, status=400)
+ except WrongMethod:
+ return HttpResponse("Only POST allowed, and not from browsers", status=405)
+ except BadIncomingRequest:
+ return JsonResponse({'reason': 'malformed json, validation failed'}, status=400)
+ except TypeError:
+ return JsonResponse({'reason': 'mispelled fieldname in JSON or unknown data sent..'}, status=400)
+ except BankAccount.DoesNotExist:
+ return JsonResponse({'reason': 'credit or debit account does not exist'}, status=404)
+ except BankAccount.MultipleObjectsReturned:
+ return JsonResponse({'reason': 'internal error (collision in db)'}, status=500)
+ except OperationalError:
+ return JsonResponse({'reason': 'internal error (bank has no db)'}, status=500)
+ except CurrencyMismatch:
+ return JsonResponse({'reason': 'currency mismatch between credit/debit account and amount'},
+ status=500)
+
+
+@csrf_exempt
+def add_incoming(request):
+ if request.method != 'POST':
+ raise WrongMethod('GET')
+ data = json.loads(request.body.decode('utf-8'))
+ Schemas.validate_incoming_request(data, Schemas.incoming_request_schema)
+ wire_transfer_in_out(data['amount'],
+ data['debit_account'],
+ data['credit_account'],
+ data['wtid'])
+ return JsonResponse({'outcome': 'ok'}, status=200)
+
+
+@is_logged
+def withdraw_attempt(request):
+ return render(request, 'withdraw.html', {'account_no': request.session["account_no"]})
+
+
+@is_logged
+def withdraw_process(request):
+ return withdraw_attempt(request)
+
+
+def create_reserve_at_exchange(request, success_url, reserve_set):
+ if not isinstance(reserve_set, Reserve):
+ return HttpResponseServerError
+ o = urlparse(reserve_set.exchange)
+ if o.scheme == '' or o.netloc == '' or o.path != '/':
+ return JsonResponse({'error': 'bad exchange base url',
+ 'given_url': reserve_set.mint}, status=400)
+ amount = {'value': reserve_set.amount['value'],
+ 'fraction': reserve_set.amount['fraction'],
+ 'currency': reserve_set.amount['currency']}
+ # Should raise ExchangeUnknown ..
+ check_exchange_account_no(reserve_set.exchange_account)
+ json_body = {'reserve_pub': reserve_set.reserve_pub,
+ 'execution_date': "/Date(" + str(int(time.time())) + ")/",
+ 'wire': {'type': reserve_set.wire_type,
+ 'account_number': reserve_set.exchange_account},
+ 'amount': amount}
+ res = requests.post(urljoin(reserve_set.exchange, '/admin/add/incoming'), json=json_body)
+ if res.status_code != 200:
+ return JsonResponse({'error': "internal error",
+ 'hint': "wire transfer failed (bad response)",
+ 'status': res.status_code,
+ 'body': res.text}, status=500)
+ wire_transfer_in_out(amount,
+ request.session['account_no'],
+ reserve_set.exchange_account,
+ "withdrawal")
+ request.session['withdrawal_successful'] = True
+ return redirect('/profile')
diff --git a/talerbank/app/history.py b/talerbank/app/history.py
new file mode 100644
index 0000000..bfe80be
--- /dev/null
+++ b/talerbank/app/history.py
@@ -0,0 +1,79 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from .lib import (get_logged_user_account,
+ get_public_accounts,
+ get_bank_account_from_username,
+ write_log_file,
+ check_db,
+ is_logged,
+ get)
+from .errors import internal_error_handler
+from django.shortcuts import (render,
+ redirect)
+
+
+def extract_history(bank_account):
+ ret = []
+ for item in bank_account.history_set.all():
+ ret.append({'float_amount': item.amount,
+ 'float_currency': item.currency,
+ 'direction': 'FROM' if item.direction == 'IN' else 'TO',
+ 'counterpart': item.counterpart,
+ 'subject': item.subject,
+ 'date': item.date.strftime("%d/%m/%y")})
+ return ret
+
+
+def history_attempt(request):
+ user_bank_account = get_logged_user_account(request.session['account_no'])
+ render_history = []
+ render_history = extract_history(user_bank_account)
+ return render(request, 'history.html', {'history': render_history})
+
+
+@check_db
+@is_logged
+def history_process(request):
+ return history_attempt(request)
+
+
+@check_db
+def public_accounts_process(request):
+ return public_accounts_attempt(request)
+
+
+def public_accounts_attempt(request):
+ accounts = []
+ for item in get_public_accounts():
+ accounts.append({'account_name': item.user.username})
+ sel_account_name = get(request.GET, 'account')
+ if not sel_account_name:
+ return redirect("/public-accounts/?account=Tor")
+ sel_account = get_bank_account_from_username(sel_account_name)
+ if not sel_account:
+ return internal_error_handler(request)
+ history = extract_history(sel_account)
+ return render(request,
+ 'public_histories_reloaded.html',
+ {'public_accounts': accounts,
+ 'selected_account':
+ {'account_name': sel_account_name,
+ 'account_no': sel_account.account_no,
+ 'history': history}
+ })
diff --git a/talerbank/app/lib.py b/talerbank/app/lib.py
new file mode 100644
index 0000000..bca4ac7
--- /dev/null
+++ b/talerbank/app/lib.py
@@ -0,0 +1,208 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from .models import BankAccount, History
+from .errors import (SubPathNotFound,
+ user_not_logged_handler,
+ ExchangeUnknown,
+ CurrencyMismatch,
+ internal_error_handler,
+ no_bank_account_handler,
+ non_existent_db_handler)
+from .config import explicit_currency
+from urllib.parse import urlparse
+from os.path import split
+from django.contrib.auth.models import User
+from django.db.utils import (OperationalError,
+ ProgrammingError)
+
+
+def get(arr, key, default=False):
+ if key not in arr:
+ return default
+ return arr[key]
+
+
+def get_pin_tan_url(request):
+ return request.build_absolute_uri("/auth/pin/question")
+
+
+# return the path's part after 'limit'
+# example: given /a/b/c and limit='b', returns 'c'
+def get_relative_parameter(request, limit):
+ path = request.path
+ l = ['']
+ while True:
+ p = split(path)
+ if p[1] == limit:
+ break
+ if p[0] == '/' and p[1] == '':
+ raise SubPathNotFound
+ l.append(p[1])
+ path = p[0]
+ return l.pop()
+
+
+def get_currency(request):
+ switcher = {'bank.demo.taler.net': 'KUDOS',
+ 'bank.test.taler.net': 'PUDOS'}
+ if not request:
+ return explicit_currency
+ return switcher.get(request.META['HTTP_HOST'], explicit_currency)
+
+
+def get_public_accounts():
+ try:
+ return BankAccount.objects.filter(is_public=True)
+ except BankAccount.DoesNotExist:
+ return []
+
+
+def check_exchange_account_no(account_no):
+ try:
+ BankAccount.objects.get(account_no=account_no)
+ except BankAccount.DoesNotExist:
+ raise ExchangeUnknown
+
+
+def get_exchange_account_no(exchange_baseurl):
+ normalized_url = normalize_exchange_url(exchange_baseurl)
+ exchange = get_bank_account_from_username(normalized_url)
+ if exchange:
+ return exchange.account_no
+ raise ExchangeUnknown
+
+
+# if the exchange has an account here, it returns its 'username'
+# (= its base url) and account number, otherwise it just replies
+# its base url
+def get_pretty_exchange_details(exchange_baseurl):
+ normalized_url = normalize_exchange_url(exchange_baseurl)
+ exchange = get_bank_account_from_username(normalized_url)
+ if exchange:
+ return normalized_url + " (account #" + str(exchange.account_no) + ")"
+ return normalized_url
+
+
+def normalize_exchange_url(exchange_baseurl):
+ exchange_username = urlparse(exchange_baseurl)
+ normalized_url = exchange_username.scheme + '://' + exchange_username.netloc
+ return normalized_url
+
+
+def get_bank_account_from_username(username):
+ try:
+ user_account = User.objects.get(username=username)
+ return user_account.bankaccount
+ except User.DoesNotExist:
+ return False
+
+
+def get_logged_user_account(number):
+ return BankAccount.objects.get(account_no=number)
+
+
+def floatify_amount(amount_dict):
+ return amount_dict['value'] + (amount_dict['fraction'] / 1000000)
+
+
+def write_log_file(data, log_file="/tmp/django.log", flag='a'):
+ f = open(log_file, flag)
+ f.write(data)
+ f.close()
+
+
+def check_db(fn):
+ def fake_query(request):
+ try:
+ User.objects.get(username='Bank')
+ except User.DoesNotExist:
+ return no_bank_account_handler(request)
+ except ProgrammingError:
+ return internal_error_handler(request,
+ "(db available but no tables in it. Run"
+ " 'taler-bank-manage --definetables')")
+ except OperationalError:
+ return non_existent_db_handler(request)
+ return fn(request)
+ return fake_query
+
+
+def is_logged(fn):
+ def check_login(request):
+ if "account_no" not in request.session:
+ return user_not_logged_handler(request)
+ return fn(request)
+ return check_login
+
+
+# FIXME to be obsoleted by 'wire_tranfer()'
+def transfer_in(amount, account_no, wtid, counterpart="unknown"):
+ account = BankAccount.objects.get(account_no=account_no)
+ float_amount = floatify_amount(amount)
+ account.balance += float_amount
+ account.save()
+ history_item = History(amount=float_amount,
+ currency=amount['currency'],
+ direction="IN",
+ counterpart=counterpart,
+ subject=wtid,
+ account=account)
+ history_item.save()
+
+
+def wire_transfer_in_out(amount,
+ debit,
+ credit,
+ wtid):
+ debit_account = BankAccount.objects.get(account_no=debit)
+ credit_account = BankAccount.objects.get(account_no=credit)
+ wire_transfer(amount,
+ debit,
+ "OUT",
+ wtid,
+ credit_account.user.username + " (account #" + str(credit) + ")")
+ wire_transfer(amount,
+ credit,
+ "IN",
+ wtid,
+ debit_account.user.username + " (account #" + str(debit) + ")")
+
+
+# transfer funds from/to 'account_no' (used as a subroutine)
+def wire_transfer(amount,
+ account_no,
+ direction,
+ wtid="not given",
+ counterpart="unknown"):
+ account = BankAccount.objects.get(account_no=account_no)
+ if account.currency != amount['currency']:
+ raise CurrencyMismatch
+ float_amount = floatify_amount(amount)
+ if "IN" == direction:
+ account.balance += float_amount
+ else:
+ account.balance -= float_amount
+ account.save()
+ history_item = History(amount=float_amount,
+ currency=amount['currency'],
+ direction=direction,
+ counterpart=counterpart,
+ subject=wtid,
+ account=account)
+ history_item.save()
diff --git a/talerbank/app/management/__init__.py b/talerbank/app/management/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/talerbank/app/management/__init__.py
diff --git a/talerbank/app/management/commands/__init__.py b/talerbank/app/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/talerbank/app/management/commands/__init__.py
diff --git a/talerbank/app/management/commands/bank_admin.py b/talerbank/app/management/commands/bank_admin.py
new file mode 100644
index 0000000..0bad3a5
--- /dev/null
+++ b/talerbank/app/management/commands/bank_admin.py
@@ -0,0 +1,59 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2106 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from random import randint
+from django.core.management.base import BaseCommand
+from django.contrib.auth.models import User
+import argparse
+
+def main(options):
+ if options['op'] != 'users':
+ print("Operation not supported")
+ return
+ if 'reset_password' not in options:
+ print("Only --reset-password allowed")
+ return
+ try:
+ username = options['reset_password'].pop(0)
+ new_password = options['reset_password'].pop(0)
+ user = User.objects.get(username=username)
+ user.set_password(new_password)
+ user.save()
+
+ except User.DoesNotExist:
+ print("Non existent user, exiting..")
+ return
+
+ except OperationalError:
+ print("Non existent DB, exiting..")
+ return
+
+class Command(BaseCommand):
+ help = "Tool for doing various administration tasks"
+
+ def add_arguments(self, parser):
+ parser.add_argument('op',
+ help="Switch on the type of operation to accomplish."
+ " Supported switches are: 'users'")
+ parser.add_argument('--reset-password',
+ nargs=2,
+ metavar=('USER', 'NEWPASS'),
+ help="Change USER's password to NEWPASS")
+
+ def handle(self, *args, **options):
+ main(options)
diff --git a/talerbank/app/management/commands/basic_accounts.py b/talerbank/app/management/commands/basic_accounts.py
new file mode 100644
index 0000000..78435ba
--- /dev/null
+++ b/talerbank/app/management/commands/basic_accounts.py
@@ -0,0 +1,26 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2106 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+from django.core.management.base import BaseCommand
+from django.contrib.auth.models import User
+from django.db.utils import OperationalError
+from ...startup import basic_accounts
+
+
+class Command(BaseCommand):
+ def handle(self, *args, **options):
+ basic_accounts()
diff --git a/talerbank/app/management/commands/check_db.py b/talerbank/app/management/commands/check_db.py
new file mode 100644
index 0000000..b8c50f9
--- /dev/null
+++ b/talerbank/app/management/commands/check_db.py
@@ -0,0 +1,46 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2106 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+from django.core.management.base import BaseCommand
+from django.contrib.auth.models import User
+from django.db.utils import OperationalError
+
+
+def check_db():
+ try:
+ bank = User.objects.get(username='Bank')
+ print ("The DB contains the bank's bank account, so it is assumed to"
+ " work properly")
+ except User.DoesNotExist:
+ print("\n*****WARNING*****\nCreate predefined accounts"
+ " before running the bank.\n"
+ "Try 'taler-bank-manage --preaccounts\n\n"
+ "**********\n")
+ return 1
+ except OperationalError:
+ print("\n*****WARNING*****\n\nPlease create 'talertest'"
+ " database before running the bank.\n"
+ "Create also predefined accounts (with"
+ " 'taler-bank-manage --preaccounts')\n\n"
+ "**********\n")
+ return 1
+ return 0
+
+
+class Command(BaseCommand):
+ def handle(self, *args, **options):
+ check_db()
diff --git a/talerbank/app/management/commands/dump_talerdb.py b/talerbank/app/management/commands/dump_talerdb.py
new file mode 100644
index 0000000..f245eed
--- /dev/null
+++ b/talerbank/app/management/commands/dump_talerdb.py
@@ -0,0 +1,57 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2106 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+from django.core.management.base import BaseCommand
+from ...models import (BankAccount,
+ History)
+from django.db.utils import OperationalError
+from time import gmtime, strftime
+
+error_message = "\n*****ERROR*****\n\nPlease create" \
+ + " the database before running this test." \
+ + "**********\n"
+
+
+def dump_accounts():
+ try:
+ accounts = BankAccount.objects.all()
+ if accounts.count() < 1:
+ print("No accounts created yet..")
+ return
+ for acc in accounts:
+ print(acc.user.username + " has account number " + str(acc.account_no))
+ except OperationalError:
+ print(error_message)
+
+def dump_history():
+ try:
+ history = History.objects.all()
+ for item in history:
+ print("Account " + item.account.user.username + ": "
+ + item.direction + " " + str(item.amount) + " "
+ + item.currency + " on " + str(item.date))
+
+
+
+ except OperationalError:
+ print(error_message)
+
+
+class Command(BaseCommand):
+ def handle(self, *args, **options):
+ dump_accounts()
+ dump_history()
diff --git a/talerbank/app/management/commands/pre_accounts.py b/talerbank/app/management/commands/pre_accounts.py
new file mode 100644
index 0000000..35e8bd1
--- /dev/null
+++ b/talerbank/app/management/commands/pre_accounts.py
@@ -0,0 +1,53 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2106 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+from django.core.management.base import BaseCommand
+from django.db.utils import DataError
+from django.contrib.auth.models import User
+from django.db.utils import OperationalError
+from ...models import BankAccount
+from ...lib import get_currency
+from ...lib import write_log_file
+from ...config import pre_accounts
+
+
+def make_accounts():
+ for u in pre_accounts:
+ print("Processing " + u)
+ try:
+ User.objects.get(username=u)
+ except OperationalError:
+ print("Please create DB before running this option")
+ return
+ except User.DoesNotExist:
+ try:
+ a = User.objects.create_user(username=u, password='')
+ except DataError:
+ print("Given username exceeded 30 chars, please make it shorter!")
+ return
+ is_public = not (u == 'http://mint.test.taler.net' or
+ u == 'http://mint.demo.taler.net')
+ b = BankAccount(user=a,
+ currency=get_currency(None),
+ is_public=is_public)
+ b.save()
+ print (" getting account number " + str(b.account_no))
+
+
+class Command(BaseCommand):
+ def handle(self, *args, **options):
+ make_accounts()
diff --git a/talerbank/app/management/commands/sample_donations.py b/talerbank/app/management/commands/sample_donations.py
new file mode 100644
index 0000000..06e4599
--- /dev/null
+++ b/talerbank/app/management/commands/sample_donations.py
@@ -0,0 +1,62 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2106 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from random import randint
+from django.core.management.base import BaseCommand
+from ...lib import (write_log_file,
+ get_public_accounts,
+ get_currency,
+ wire_transfer_in_out)
+from ...config import expects_donations
+from ...models import BankAccount
+
+
+def sample_donations():
+ public_accounts = get_public_accounts()
+ for account in public_accounts:
+ for i in range(0, 9):
+ if account.user.username in expects_donations:
+ value = randint(1, 100)
+ try:
+ # 1st take money from bank and give to exchange
+ wire_transfer_in_out({'value': value,
+ 'fraction': 0,
+ 'currency': get_currency(None)},
+ 1,
+ 2,
+ "Test withdrawal")
+
+ # 2nd take money from exchange and give to donation receiver
+ wire_transfer_in_out({'value': value,
+ 'fraction': 0,
+ 'currency': get_currency(None)},
+ 2,
+ account.account_no,
+ "Test donation(s)")
+ except BankAccount.DoesNotExist:
+ print("Please create basic accounts before. Run taler-bank-manage --preaccounts")
+ return
+ except OperationalError:
+ print("No DB found, please create one beforehand. Try with taler-bank-manage --createdb")
+ return
+
+
+
+class Command(BaseCommand):
+ def handle(self, *args, **options):
+ sample_donations()
diff --git a/talerbank/app/migrations/0001_initial.py b/talerbank/app/migrations/0001_initial.py
new file mode 100644
index 0000000..d92797c
--- /dev/null
+++ b/talerbank/app/migrations/0001_initial.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-04-08 14:17
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='BankAccount',
+ fields=[
+ ('is_public', models.BooleanField(default=False)),
+ ('balance', models.FloatField(default=0)),
+ ('currency', models.CharField(default='', max_length=12)),
+ ('account_no', models.AutoField(primary_key=True, serialize=False)),
+ ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='History',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('amount', models.FloatField(default=0)),
+ ('currency', models.CharField(max_length=12)),
+ ('direction', models.CharField(max_length=4)),
+ ('counterpart', models.CharField(default='unknown', max_length=200)),
+ ('subject', models.CharField(default='not given', max_length=200)),
+ ('date', models.DateField(auto_now=True)),
+ ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.BankAccount')),
+ ],
+ ),
+ ]
diff --git a/talerbank/app/migrations/__init__.py b/talerbank/app/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/talerbank/app/migrations/__init__.py
diff --git a/talerbank/app/models.py b/talerbank/app/models.py
new file mode 100644
index 0000000..45bffdb
--- /dev/null
+++ b/talerbank/app/models.py
@@ -0,0 +1,52 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from __future__ import unicode_literals
+from django.contrib.auth.models import User
+from django.db import models
+
+
+class BankAccount(models.Model):
+ is_public = models.BooleanField(default=False)
+ balance = models.FloatField(default=0)
+ currency = models.CharField(max_length=12, default="")
+ # this value (below) needs not to be unique in some cases: for
+ # example when an http and https exchanges want to have the same
+ # bank account number
+ account_no = models.AutoField(primary_key=True)
+ user = models.OneToOneField(User,
+ on_delete=models.CASCADE)
+
+
+class History(models.Model):
+ amount = models.FloatField(default=0)
+ currency = models.CharField(max_length=12)
+ direction = models.CharField(max_length=4)
+ counterpart = models.CharField(default="unknown", max_length=200)
+ subject = models.CharField(default="not given", max_length=200)
+ date = models.DateField(auto_now=True)
+ account = models.ForeignKey(BankAccount, on_delete=models.CASCADE)
+
+ def set_balance(self, new_balance):
+ if isinstance(new_balance, int) or isinstance(new_balance, float):
+ self.balance = new_balance
+ else:
+ print("Not a number given for new balance")
+
+ def push_history(self, obj):
+ self.history.append(obj)
diff --git a/talerbank/app/my-static/favicon.ico b/talerbank/app/my-static/favicon.ico
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/talerbank/app/my-static/favicon.ico
diff --git a/talerbank/app/my-static/style.css b/talerbank/app/my-static/style.css
new file mode 100644
index 0000000..923654b
--- /dev/null
+++ b/talerbank/app/my-static/style.css
@@ -0,0 +1,158 @@
+/*
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+ @author Gabor Toth
+*/
+
+body {
+ background-color: white;
+ margin: 0;
+ padding: 0;
+ font-family: Verdana, sans;
+}
+
+header {
+ width: 100%;
+ height: 100px;
+ margin: 0;
+ padding: 0;
+ border-bottom: 1px solid black;
+}
+
+header h1 {
+ font-size: 200%;
+ margin: 15 0 0 120px;
+/* position: relative;
+ top: 50%;
+ transform: translateY(-50%);*/
+}
+header #logo {
+ float: left;
+ width: 100px;
+ height: 100px;
+ padding: 0;
+ margin: 0;
+ text-align: center;
+ border-right: 1px solid black;
+}
+
+section#menu {
+ margin: 0 0 90 0;
+ padding: 5px;
+ border-right: 1px solid black;
+ height: 100%;
+ width: 90px;
+ float: left;
+}
+
+section#main {
+ margin: 0 0 0 100px;
+ padding: 20px;
+ border-left: 1px solid black;
+ height: 100%;
+ max-width: 40em;
+}
+
+section#main h1:first-child {
+ margin-top: 0;
+}
+
+div.login-form, div.register-form {
+ border-radius: 10px;
+ background-color: #f2f2f2;
+ padding: 11px 27px 44px 27px;
+ max-width: 200px;
+}
+
+div.login-form input[type=submit],
+div.register-form input[type=submit] {
+ width-max: 30%;
+ float: right;
+ padding: 6px;
+}
+
+.selected-item {
+ border-style: solid;
+ border-width: 1px;
+}
+
+.informational {
+ border-radius: 8px;
+ padding: 8px;
+}
+
+.informational-ok {
+ background: #ccffcc;
+}
+
+.informational-fail {
+ background: #ff8566;
+}
+
+div.login-form input[type=text],
+div.login-form input[type=password],
+div.register-form input[type=text],
+div.register-form input[type=password] {
+ width: 100%;
+ padding: 12px 20px;
+ margin: 8px 0;
+ display: inline-block;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-sizing: border-box;
+}
+
+h1 {
+ font-size: 160%;
+}
+
+h2 {
+ font-size: 140%;
+}
+
+h3 {
+ font-size: 120%;
+}
+
+h4, h5, h6 {
+ font-size: 100%;
+}
+
+table.history {
+ margin: 30px 0px;
+ border-width: 0px;
+ border-spacing: 3px;
+ border-style: groove;
+ border-color: gray;
+ border-collapse: separate;
+ background-color: white;
+}
+table.history th {
+ border-width: 1px;
+ padding: 5px;
+ border-style: outset;
+ border-color: gray;
+ background-color: white;
+ -moz-border-radius: ;
+}
+table.history td {
+ border-width: 1px;
+ padding: 5px;
+ border-style: outset;
+ border-color: gray;
+ background-color: white;
+ -moz-border-radius: ;
+}
diff --git a/talerbank/app/schemas.py b/talerbank/app/schemas.py
new file mode 100644
index 0000000..abec0d3
--- /dev/null
+++ b/talerbank/app/schemas.py
@@ -0,0 +1,73 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+import validictory
+from .errors import (BadIncomingRequest,
+ BadWireDetails,
+ BadAmount)
+
+# definitions of JSON schemas for validating data
+
+
+class Schemas:
+ wiredetails_schema = {"type": "object",
+ "properties": {
+ "test": {"type": "object",
+ "properties" : {
+ "type" : {"type": "string"},
+ "account_number": {"type": "integer"},
+ "bank_uri": {"type": "string"}}}}}
+
+
+ amount_schema = {"type": "object",
+ "properties": {
+ "value": {"type": "integer"},
+ "fraction": {"type": "integer"},
+ "currency": {"type": "string"}}}
+
+ incoming_request_schema = {"type": "object",
+ "properties": {
+ "amount": {"type": amount_schema},
+ "wtid": {"type": "string"},
+ "credit_account": {"type": "integer"},
+ "debit_account": {"type": "integer"}}}
+
+ @staticmethod
+ def validate_amount(amount, schema):
+ try:
+ validictory.validate(amount, schema)
+ except (ValueError, TypeError):
+ raise BadAmount
+ return True
+
+ @staticmethod
+ def validate_wiredetails(wiredetails, schema):
+ try:
+ validictory.validate(wiredetails, schema)
+ except (ValueError, TypeError):
+ raise BadWireDetails
+ return True
+
+
+ @staticmethod
+ def validate_incoming_request(incoming_request, schema):
+ try:
+ validictory.validate(incoming_request, schema)
+ except (ValueError, TypeError):
+ raise BadIncomingRequest
+ return True
diff --git a/talerbank/app/startup.py b/talerbank/app/startup.py
new file mode 100644
index 0000000..98498de
--- /dev/null
+++ b/talerbank/app/startup.py
@@ -0,0 +1,58 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+ @file Container for routines to be run at startup
+"""
+
+from .models import BankAccount
+from django.contrib.auth.models import User
+from django.db.utils import OperationalError
+from .lib import get_currency
+import sys
+
+# Creates basic accounts (bank's and exchange's) if the user didn't manually
+
+def basic_accounts():
+ try:
+ if sys.argv[1] != 'runserver' and sys.argv[1] != 'basic_accounts' \
+ and sys.argv[1] != 'sample_donations':
+ print("basic_accounts() not supposed to be fired..")
+ return
+ except IndexError:
+ print("Got IndexError, unmanaged..")
+ return
+ try:
+ bank = User.objects.get(username='Bank')
+ print("Basic accounts found\n")
+ except OperationalError:
+ print("Please create DB before running the bank")
+ sys.exit()
+ # bank not existent, so neither other accounts are
+ except User.DoesNotExist:
+ print("Basic accounts not found..\n")
+ bank = User.objects.create_user(username='Bank', password='')
+ exchange = User.objects.create_user(username='Exchange', password='')
+ bank_ba = BankAccount(user=bank,
+ currency=get_currency(None),
+ is_public=True)
+ bank_ba.save()
+ print("Creating '" + bank_ba.user.username + "' account, with number " + str(bank_ba.account_no) + "\n")
+ exchange_ba = BankAccount(user=exchange,
+ currency=get_currency(None),
+ is_public=True)
+ exchange_ba.save()
+ print("Creating '" + exchange_ba.user.username + "' account, with number " + str(exchange_ba.account_no) + "\n")
+
diff --git a/talerbank/app/templates/account_disabled.html b/talerbank/app/templates/account_disabled.html
new file mode 100644
index 0000000..01a59b7
--- /dev/null
+++ b/talerbank/app/templates/account_disabled.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+<html>
+ <head>
+ <title>{{ currency }} Bank - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="/static/web-common/style.css">
+ <link rel="stylesheet" type="text/css" href="/static/style.css">
+ </head>
+ <body>
+ <header>
+ <a href="/"><div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" />
+ <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta">
+ B
+ </text>
+ </svg>
+ </div></a>
+ <!--#include virtual="static/web-common/dropdown-navbar.html" -->
+ <h1 class="nav">Account disabled<h1>
+ </header>
+ <aside class="sidebar" id="left">
+ </aside>
+ <section id="main">
+ <article>
+ <h1>Account disabled</h1>
+ <p>{{ name }}, your account has been disabled, <a href="/register">register</a>
+ a new one!</p>
+ </article>
+ </section>
+ </body>
+</html>
diff --git a/talerbank/app/templates/central_page.html b/talerbank/app/templates/central_page.html
new file mode 100644
index 0000000..1fa9ee9
--- /dev/null
+++ b/talerbank/app/templates/central_page.html
@@ -0,0 +1,237 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+{% load staticfiles %}
+<html lang="en">
+<head>
+ <title>{{ currency }} Bank - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="/static/web-common/style.css">
+ <link rel="stylesheet" type="text/css" href="/static/style.css">
+ <script type="application/javascript">
+ function onSuccess() {
+ console.log("installation successful");
+ }
+
+ function onFailure(detail) {
+ console.log("installation failed", detail);
+ }
+
+ function installWallet() {
+ chrome.webstore.install("https://chrome.google.com/webstore/detail/millncjiddlpgdmkklmhfadpacifaonc", onSuccess, onFailure);
+ }
+ </script>
+ <script src="{% static "web-common/taler-presence.js" %}" type="application/javascript"></script>
+ <script type="application/javascript">
+
+ var bank_currency = "{{ currency }}";
+ function addOption(value, currency) {
+ var s = document.getElementById("reserve-amount");
+ var e = document.createElement("option");
+ e.textContent = "".concat(value, " ", currency);
+ e.value = value;
+ s.appendChild(e);
+ }
+
+ function addOptions() {
+ addOption(1, bank_currency);
+ addOption(5, bank_currency);
+ addOption(10, bank_currency);
+ addOption(20, bank_currency);
+ }
+
+ function requestCreateReserve() {
+ var form = document.getElementById("reserve-form");
+
+ var wallet_param = {
+ callback_url: "{{ pin_tan_url }}",
+ amount: {
+ value: parseInt(form.elements["reserve-amount"].value),
+ fraction: 0,
+ currency: "{{ currency }}"
+ },
+ wt_types: ["TEST"]
+ };
+ var event = new CustomEvent("taler-create-reserve", {
+ detail: wallet_param
+ });
+ document.dispatchEvent(event);
+ };
+
+ function confirmReserve(reserve_pub) {
+ var event = new CustomEvent("taler-confirm-reserve", {
+ detail: {
+ reserve_pub: reserve_pub
+ }
+ });
+ document.dispatchEvent(event);
+ }
+
+ /* The following function is useful to parse fractional
+ parts of amounts */
+ function parseAmount(amount_str) {
+ var re = /([0-9]+)(\.?[0-9][0-9]?)? ([A-Z]+)/;
+ var amount = re.exec(amount_str);
+ if (amount == null || amount[0] != amount_str){
+ window.alert("Incorrect amount entered, give in the"
+ + " form 'XY.Z EUR' or 'XY EUR'");
+ return null;
+ }
+ var amount_fraction = 0;
+ if (amount[2] != null) // fractional part given
+ amount_fraction = Number("0." + amount[2]) * 1000000;
+ if (amount[1] + amount_fraction == 0)
+ return null;
+ return {value: Number(amount[1]),
+ fraction: amount_fraction,
+ currency: amount[amount.length - 1]};
+ };
+
+ document.addEventListener("DOMContentLoaded", addOptions);
+
+ </script>
+
+ <style type="text/css">
+
+ a[disabled="true"] {
+ pointer-events: none;
+ color: grey;
+ }
+ </style>
+</head>
+<body>
+ <header>
+ <a href="/"><div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" />
+ <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta">
+ B
+ </text>
+ </svg>
+ </div></a>
+ <!--#include virtual="/static/web-common/dropdown-navbar.html" -->
+ <h1 class="nav">Welcome <em>{{ name }}</em>!</h1>
+ </header>
+ <section id="menu">
+ <a href="/logout">[Logout]</a><br>
+ <p>
+ Account:<br>
+ # {{ account_no }}
+ </p>
+ <p>Current<br>
+ balance:<br>
+ <b>
+ {{ balance }}</b> <br>
+ {{ currency }}
+ </p>
+ </section>
+ <section id="main">
+ <article>
+ <div class="notification">
+ {% if withdraw %}
+ {% if withdraw == "success" %}
+ <p class="informational informational-ok">
+ Withdrawal approved!
+ </p>
+ <script>confirmReserve("{{ reserve_pub }}");</script>
+ {% else %}
+ <p class="informational informational-fail">
+ Withdrawal failed!
+ </p>
+ {% endif %}
+ {% endif %}
+ {% if registration %}
+ {% if registration == "success" %}
+ <p class="informational informational-ok">
+ Registration successful!
+ </p>
+ {% endif %}
+ {% endif %}
+ </div>
+ </article>
+ <article>
+ <div class="taler-installed-hide">
+ <h2>Withdraw digital coins using Taler</h2>
+ You need to install the Taler wallet to withdraw digital coins.
+ <span id="install-done" style="visibility: hidden">(done)</span>
+ <ul>
+ <li>from the app store for <a
+ href="https://chrome.google.com/webstore/detail/gnu-taler-wallet/millncjiddlpgdmkklmhfadpacifaonc">Google
+ Chrome and Chromium</a>
+ </li>
+ <li>By visiting our <a href="/landing">landing page</a>.
+ </li>
+ </ul>
+ Right now, you need Chrome or Chromium to
+ install the wallet. We plan to support other browsers in the future.
+ </div>
+
+ <div class="taler-installed-show">
+ <h2>Withdraw digital coins using Taler</h2>
+
+ <form id="reserve-form" name="tform" action="">
+ {% csrf_token %}
+ <input type="text" id="reserve-pk-input" name="reserve_pk" hidden></input>
+ Amount to withdraw:
+ <select id="reserve-amount" name="kudos_amount">
+ <!-- programmatically filled -->
+ </select>
+ <input type="text" name="mint_rcv" id="kudos-mint" hidden></input>
+ <input id="select-mint"
+ class="taler-installed-activate"
+ type="button"
+ onclick="requestCreateReserve()"
+ value="Select exchange provider"></input>
+ </form>
+ </div>
+ </article>
+ <article>
+ <h2>Transaction history</h2>
+ <div id="transactions-history">
+ {% if history %}
+ <table class="history">
+ <tbody>
+ <tr>
+ <th style="text-align:center">Date</th>
+ <th style="text-align:center">Amount</th>
+ <th style="text-align:center">Counterpart</th>
+ <th style="text-align:center">Subject</th>
+ </tr>
+ {% for item in history %}
+ <tr>
+ <td style="text-align:right">{{ item.date }}</td>
+ <td style="text-align:right">
+ {% if item.direction == "FROM" %}+{% else %}-{% endif %}{{ item.float_amount }}
+ {{ item.float_currency }}
+ </td>
+ <td class="text-align:left">{{ item.counterpart }}</td>
+ <td class="text-align:left">{{ item.subject }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <p class="informational informational-fail">
+ No transactions made to/from this account
+ </p>
+ {% endif %}
+ </div>
+
+ </article>
+ </section>
+</body>
+</html>
diff --git a/talerbank/app/templates/error.html b/talerbank/app/templates/error.html
new file mode 100644
index 0000000..1878583
--- /dev/null
+++ b/talerbank/app/templates/error.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+{% load staticfiles %}
+<html lang="en">
+<head>
+ <title>{{ currency }} Bank - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="/static/web-common/style.css">
+ <link rel="stylesheet" type="text/css" href="/static/style.css">
+</head>
+<body>
+ <header>
+ <a href="/"><div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" />
+ <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta">
+ B
+ </text>
+ </svg>
+ </div></a>
+ <!--#include virtual="/static/web-common/dropdown-navbar.html" -->
+ <h1>Error</h1>
+ </header>
+ <aside class="sidebar" id="left">
+ </aside>
+ <section id="main">
+ <article>
+ <div>
+ <h3>
+ {% if type = "wrong_pin" %}
+ Wrong PIN
+ {% elif type = "non_supported_method" %}
+ Non supported wire method
+ {% elif type = "internal_error" %}
+ Internal error
+ {% elif type = "exchange_unknown" %}
+ Exchange unknown
+ {% elif type = "not_logged" %}
+ User not logged
+ {% elif type = "custom" %}
+ {% if not custom_title %}
+ Error
+ {% else %}
+ {{ custom_title }}
+ {% endif %}
+ {% endif %}
+ </h3>
+ <p>
+ {% if type = "wrong_pin" %}
+ Return to your <a href="/profile">profile page</a>
+ {% elif type = "internal_error" %}
+ Resource unavailable {{ hint }}
+ {% elif type = "non_supported_method" %}
+ This bank supports TEST wire method only
+ {% elif type = "exchange_unknown" %}
+ Given exchange does not exist (try 'http' instead of 'https'?) <!---ok, not user-friendly but informative for developers -->
+ {% elif type = "not_logged" %}
+ Operation not possible, log in at <a href="/">home page</a>
+ {% elif type = "custom" %}
+ {% if not custom_message %}
+ error details not given
+ {% else %}
+ {{ custom_message }}
+ {% endif %}
+ {% endif %}
+ </p>
+ </div>
+ </article>
+ </section>
+</body>
diff --git a/talerbank/app/templates/history.html b/talerbank/app/templates/history.html
new file mode 100644
index 0000000..d6d6ddc
--- /dev/null
+++ b/talerbank/app/templates/history.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+<html>
+ <head>
+ </head>
+ <body>
+ {% if history %}
+ <ul>
+ {% for item in history %}
+ <li> {{ item.float_amount }} {{ item.float_currency }} {{ item.direction }} {{ item.counterpart }}</li>
+ {% endfor %}
+ </ul>
+ {% else %}
+ No transactions made to/from this account
+ {% endif %}
+ </body>
+<html>
diff --git a/talerbank/app/templates/home_page.html b/talerbank/app/templates/home_page.html
new file mode 100644
index 0000000..a0e9417
--- /dev/null
+++ b/talerbank/app/templates/home_page.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+{% load staticfiles %}
+<html lang="en">
+<head>
+ <title>{{ currency }} Bank - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="/static/web-common/style.css">
+</head>
+<body>
+ <header>
+ <a href="/"><div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" />
+ <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta">
+ B
+ </text>
+ </svg>
+ </div></a>
+ <!--#include virtual="/static/web-common/dropdown-navbar.html"-->
+ <h1 class="nav">Welcome to the {{ currency }} Bank!</h1>
+ </header>
+ <aside class="sidebar" id="left">
+ </aside>
+ <section id="main">
+ <article>
+ <div class="login-form">
+ <h1>Please login!</h1>
+ {% if wrong %}
+ <p class="informational informational-fail">
+ Some fields were either not filled or filled incorrectly.
+ Please try again.
+ </p>
+ {% endif %}
+ {% if logged_out %}
+ <p class="informational informational-ok">
+ Successfully logged out!
+ </p>
+ {% endif %}
+ <table>
+ <form method="post" action="/login/">
+ {% csrf_token %}
+ <input type="text" name="username" placeholder="username"></input>
+ <input type="password" name="password" placeholder="password"></input>
+ <input type="submit" value="Ok"></input>
+ </form>
+ </table>
+ </div>
+ <p>
+ If you are a new customer, please <a href="/register/">register</a>.
+ Registration is fast and gratis, and it gives you a registration bonus
+ of 100 {{ currency }}!
+ </p>
+ <p>
+ To view account histories for public accounts,
+ please <a href="/public-accounts">click here</a>.
+ </p>
+ </article>
+ </section>
+</body>
diff --git a/talerbank/app/templates/logout.html b/talerbank/app/templates/logout.html
new file mode 100644
index 0000000..e238853
--- /dev/null
+++ b/talerbank/app/templates/logout.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+{% load staticfiles %}
+<html lang="en">
+<head>
+ <title>{{ currency }} Bank - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="static/web-common/style.css">
+</head>
+<body>
+ <header>
+ <a href="/"><div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" />
+ <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta">
+ B
+ </text>
+ </svg>
+ </div></a>
+ </header>
+ <aside class="sidebar" id="left">
+ </aside>
+ <section id="main">
+ <article>
+ <div>
+ <h3>Logged out</h3>
+ redirecting to <a href="/">home page</a>
+ </div>
+ </article>
+ </section>
+ <script type="application/javascript">
+ function redirectHomepage(){
+ window.location.href = "/";}
+ setTimeout(redirectHomepage, 2500);
+ </script>
+</body>
diff --git a/talerbank/app/templates/pin_tan.html b/talerbank/app/templates/pin_tan.html
new file mode 100644
index 0000000..37a1465
--- /dev/null
+++ b/talerbank/app/templates/pin_tan.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+{% load staticfiles %}
+<html>
+ <head>
+ <title>{{ currency }} Bank - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="/static/web-common/style.css">
+ <link rel="stylesheet" type="text/css" href="/static/style.css">
+ </head>
+ <body>
+ <header>
+ <a href="/"><div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" />
+ <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta">
+ B
+ </text>
+ </svg>
+ </div></a>
+ <!--#include virtual="/static/web-common/dropdown-navbar.html" -->
+ <h1 class="nav">PIN/TAN: Confirm transaction</h1>
+ </header>
+ <aside class="sidebar" id="left">
+ </aside>
+ <section id="main">
+ <article>
+ <p>
+ {{ currency }} Bank needs to verify that you
+ intend to withdraw <b>{{ amount }} {{ currency }}</b> from
+ <b>{{ exchange }}</b>.
+ To prove that you are the account owner, please answer the
+ following &quot;security question&quot; (*):
+ </p>
+ <form method="post" action="/pin/verify">
+ {% csrf_token %}
+ {{ form.pin }}
+ <input type="submit" value="Ok"></input>
+ </form>
+ <small style="margin: 40px 0px">(*) A real bank should ask for
+ a PIN/TAN instead of a simple calculation. For example by sending
+ a one time password to the customer's mobile or providing her a
+ random password generator.
+ <small>
+ </article>
+ </section>
+ </body>
+</html>
diff --git a/talerbank/app/templates/public_account_details.html b/talerbank/app/templates/public_account_details.html
new file mode 100644
index 0000000..7949be6
--- /dev/null
+++ b/talerbank/app/templates/public_account_details.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+{% load staticfiles %}
+<html lang="en">
+<head>
+ <title>{{ currency }} Bank - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="/static/web-common/style.css">
+ <link rel="stylesheet" type="text/css" href="/static/style.css">
+</head>
+<body>
+ <header>
+ <a href="/"><div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" />
+ <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta">
+ B
+ </text>
+ </svg>
+ </div></a>
+ <!--#include virtual="/static/web-common/dropdown-navbar.html" -->
+ <h1>Public account '{{ account_name }}'</h1>
+ </header>
+ <aside class="sidebar" id="left">
+ </aside>
+ <section id="main">
+ <article>
+ <div>
+ <h3>
+ Account number of {{ account_name }}
+ </h3>
+ <p>{{ account_no }}</p>
+ </div>
+ </article>
+ </section>
+</body>
diff --git a/talerbank/app/templates/public_histories_reloaded.html b/talerbank/app/templates/public_histories_reloaded.html
new file mode 100644
index 0000000..4551326
--- /dev/null
+++ b/talerbank/app/templates/public_histories_reloaded.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+<html>
+ <head>
+ <title>KUDOS Bank - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="/static/web-common/style.css">
+ <link rel="stylesheet" type="text/css" href="/static/style.css">
+ </head>
+ <body>
+ <header>
+ <a href="/"><div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" />
+ <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta">
+ B
+ </text>
+ </svg>
+ </div></a>
+ <!--#include virtual="/static/web-common/dropdown-navbar.html" -->
+ <h1 class="nav">History of public accounts</h1>
+ </header>
+ <aside class="sidebar" id="left">
+ </aside>
+ <section id="main">
+ <article>
+ <table bgcolor="#E0E0E0" width="100%" width="100%" border="0" cellpadding="2" cellspacing="1">
+ <tr>
+ {% for item in public_accounts %}
+ <td width="12%" align="center">
+ <a id="{{ item.account_name }}"
+ href="/public-accounts/?account={{ item.account_name }}">
+ {{ item.account_name }}
+
+ </a>
+ </td>
+ {% endfor %}
+ </tr>
+ </table>
+ <div id="transactions-history">
+ {% if selected_account.history %}
+ <table class="history">
+ <tr>
+ <th style="text-align:center">Date</th>
+ <th style="text-align:center">Amount</th>
+ <th style="text-align:center">Counterpart</th>
+ <th style="text-align:center">Subject</th>
+ </tr>
+ {% for entry in selected_account.history %}
+ <tr>
+ <td style="text-align:right">{{entry.date}}</td>
+ <td style="text-align:right">
+ {% if entry.direction == "FROM" %}+{% else %}-{% endif %}
+ {{ entry.float_amount }} {{ entry.float_currency }}
+ </td>
+ <td style="text-align:left">{{ entry.counterpart }}</td>
+ <td style="text-align:left">{{ entry.subject }}</td>
+ </tr>
+ {% endfor %}
+ {% else %}
+ <p>No history for this account yet</p>
+ {% endif %}
+ </div>
+ </table>
+ </article>
+ </section>
+ <script type="application/javascript">
+ function bold_selected(){
+ var target = document.getElementById("{{ selected_account.account_name }}");
+ target.style.fontWeight = "bold";
+ console.log ("selecting", target.parentNode);
+ target.parentNode.setAttribute("class", "selected-item");
+ };
+ document.addEventListener('DOMContentLoaded', bold_selected);
+ </script>
+ </body>
+</html>
diff --git a/talerbank/app/templates/register.html b/talerbank/app/templates/register.html
new file mode 100644
index 0000000..f48868c
--- /dev/null
+++ b/talerbank/app/templates/register.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+{% load staticfiles %}
+<html lang="en">
+ <head>
+ <title>{{ currency }} Bank - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="/static/web-common/style.css">
+ <link rel="stylesheet" type="text/css" href="/static/style.css">
+ </head>
+ <body>
+ <header>
+ <a href="/"><div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" />
+ <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta">
+ B
+ </text>
+ </svg>
+ </div></a>
+ <!--#include virtual="/static/web-common/dropdown-navbar.html" -->
+ <h1 class="nav">Register to the {{ currency }} bank!</h1>
+ </header>
+ <aside class="sidebar" id="left">
+ </aside>
+ <section id="main">
+ <article>
+ <div class="notification">
+ {% if wrong %}
+ <p class="informational informational-fail">
+ Some fields were either not filled or filled incorrectly.
+ </p>
+ {% endif %}
+ {% if not_available %}
+ <p class="informational informational-fail">
+ Sorry, this username is no longer available.
+ Please choose another one!
+ </p>
+ {% endif %}
+ </div>
+ </article>
+ </section>
+ <section id="main">
+ <article>
+ <div class="register-form">
+ <h1>Registration form</h1>
+ <form method="post" action="/register/">
+ {% csrf_token %}
+ <input type="text" name="username" placeholder="username"></input>
+ <input type="password" name="password" placeholder="password"></input>
+ <input type="submit" value="Ok"></input>
+ </form>
+ </div>
+ </article>
+ </section>
+ </body>
+</html>
diff --git a/talerbank/app/test.py b/talerbank/app/test.py
new file mode 100644
index 0000000..6ad4faa
--- /dev/null
+++ b/talerbank/app/test.py
@@ -0,0 +1,31 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from .lib import check_db
+
+
+def check_db_test_process(request):
+ """this indirection is needed since is not possible
+ to call "decorated" functions without importing the
+ decorator in urls.py as well"""
+ return check_db_test_attempt(request)
+
+
+@check_db
+def check_db_test_attempt(request):
+ pass
diff --git a/talerbank/app/tests.py b/talerbank/app/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/talerbank/app/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/talerbank/app/urls.py b/talerbank/app/urls.py
new file mode 100644
index 0000000..4970494
--- /dev/null
+++ b/talerbank/app/urls.py
@@ -0,0 +1,37 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from django.conf.urls import url
+from . import views
+
+urlpatterns = [
+ url(r'^$', views.home_page),
+ url(r'^favicon\.ico$', views.ignore),
+ url(r'check-db-test', views.check_db_test_process),
+ url(r'register', views.register_process),
+ url(r'login', views.login_process),
+ url(r'logout', views.logout_process),
+ url(r'withdraw', views.withdraw_process),
+ url(r'public-accounts/details', views.view_public_accno_process),
+ url(r'public-accounts', views.public_accounts_process),
+ url(r'pin/question', views.pin_tan_question),
+ url(r'pin/verify', views.pin_tan_verify),
+ url(r'add/incoming', views.add_incoming_attempt),
+ url(r'profile', views.profile_process),
+ url(r'history', views.history_process)
+ ]
diff --git a/talerbank/app/user_mgmt.py b/talerbank/app/user_mgmt.py
new file mode 100644
index 0000000..6c8b800
--- /dev/null
+++ b/talerbank/app/user_mgmt.py
@@ -0,0 +1,147 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+from django.contrib.auth import (authenticate,
+ login,
+ logout)
+from django.contrib.auth.models import User
+from .lib import (write_log_file,
+ is_logged,
+ check_db,
+ get,
+ get_currency)
+from .util import (get_central_page,
+ LoginForm)
+from .models import BankAccount
+from .errors import bad_get_parameter_handler
+from .lib import (wire_transfer_in_out,
+ get_bank_account_from_username)
+from django.shortcuts import (render,
+ redirect)
+from django import forms
+from random import randint
+import django.db
+
+
+class UserReg(forms.Form):
+ username = forms.CharField()
+ password = forms.CharField(widget=forms.PasswordInput())
+
+
+# register a new user giving 100 KUDOS bonus
+def register_attempt(request):
+ wrong_field = False
+ not_available = False
+ if request.method == 'POST':
+ if "account_no" in request.session:
+ return get_central_page(request)
+ form = UserReg(request.POST)
+ if form.is_valid():
+ try:
+ user = User.objects.create_user(username=form.cleaned_data['username'],
+ password=form.cleaned_data['password'])
+ account = BankAccount(user=user,
+ currency=get_currency(request))
+ account.save()
+ wire_transfer_in_out({'value': 100,
+ 'fraction': 0,
+ 'currency': get_currency(request)},
+ 1,
+ account.account_no,
+ "Joining bonus")
+ request.session['account_no'] = account.account_no
+ request.session['registration_successful'] = True
+ return redirect("/auth/profile")
+ except django.db.IntegrityError:
+ not_available = True
+ else:
+ wrong_field = True
+ return render(request,
+ 'register.html',
+ {'wrong': wrong_field,
+ 'currency': get_currency(request),
+ 'not_available': not_available})
+
+
+def login_attempt(request):
+ if "account_no" in request.session:
+ return get_central_page(request)
+ if request.method == 'POST':
+ username = request.POST['username']
+ password = request.POST['password']
+ user = authenticate(username=username, password=password)
+ if user is not None:
+ if user.is_active:
+ login(request, user)
+ request.session["account_no"] = user.bankaccount.account_no
+ return get_central_page(request)
+ else:
+ return render(request, 'account_disabled.html', {'name': user.username,
+ 'currency': get_currency(request)})
+ else:
+ request.session['wrong_login'] = True
+ return redirect("/")
+ else:
+ form = LoginForm()
+ return render(request, 'login.html', {'form': form, 'currency': get_currency(request)})
+
+
+def logout_attempt(request):
+ del request.session["account_no"]
+ logout(request)
+ request.session['logged_out'] = True
+ return redirect("/")
+
+
+def view_public_accno_attempt(request):
+ account_name = get(request.GET, 'account')
+ if not account_name:
+ return bad_get_parameter_handler(request)
+ # FIXME the following function must be exception-driven
+ bank_account = get_bank_account_from_username(account_name)
+ if not bank_account or not bank_account.is_public:
+ return render(request,
+ 'error.html',
+ {'type': 'custom',
+ 'custom_title': "Unknown public account",
+ 'custom_message': "Wrong account name given"},
+ status=405)
+ return render(request,
+ 'public_account_details.html',
+ {'account_no': bank_account.account_no,
+ 'account_name': account_name})
+
+
+@check_db
+def login_process(request):
+ return login_attempt(request)
+
+
+@check_db
+@is_logged
+def logout_process(request):
+ return logout_attempt(request)
+
+
+@check_db
+def register_process(request):
+ return register_attempt(request)
+
+
+@check_db
+def view_public_accno_process(request):
+ return view_public_accno_attempt(request)
diff --git a/talerbank/app/util.py b/talerbank/app/util.py
new file mode 100644
index 0000000..65b4e2c
--- /dev/null
+++ b/talerbank/app/util.py
@@ -0,0 +1,82 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+from django.shortcuts import render
+from .lib import (get_logged_user_account,
+ get_pin_tan_url,
+ is_logged,
+ get_currency)
+from .history import extract_history
+from django import forms
+
+
+class LoginForm(forms.Form):
+ name = forms.CharField()
+ password = forms.CharField(widget=forms.PasswordInput())
+
+
+# 'wrong' parameter getting obsolete..
+def home_page(request, wrong=False):
+ if "wrong_login" in request.session:
+ wrong = request.session['wrong_login']
+ del request.session['wrong_login']
+ if "logged_out" in request.session:
+ del request.session['logged_out']
+ return render(request,
+ 'home_page.html',
+ {'currency': get_currency(request),
+ 'logged_out': True})
+ if "account_no" in request.session:
+ return get_central_page(request)
+ return render(request,
+ 'home_page.html',
+ {'currency': get_currency(request),
+ 'wrong': wrong})
+
+
+@is_logged
+def profile_process(request):
+ return redirect_to_central_page(request)
+
+
+def redirect_to_central_page(request):
+ if 'registration_successful' in request.session:
+ del request.session['registration_successful']
+ return get_central_page(request, registration="success")
+ if 'withdrawal_successful' in request.session:
+ del request.session['withdrawal_successful']
+ return get_central_page(request, withdraw="success")
+ return get_central_page(request)
+
+
+""" 'withdraw' is needed to inform the user about the
+outcome of some withdrawal """
+
+
+def get_central_page(request, withdraw=False, registration=False):
+ user_account = get_logged_user_account(request.session['account_no'])
+ history = extract_history(user_account)
+ reserve_pub = request.session.get('reserve_pub')
+ return render(request, 'central_page.html', {'name': user_account.user.username,
+ 'balance': user_account.balance,
+ 'currency': user_account.currency,
+ 'account_no': user_account.account_no,
+ 'pin_tan_url': get_pin_tan_url(request),
+ 'history': history,
+ 'withdraw': withdraw,
+ 'registration': registration,
+ 'reserve_pub': reserve_pub})
diff --git a/talerbank/app/views.py b/talerbank/app/views.py
new file mode 100644
index 0000000..b45b4af
--- /dev/null
+++ b/talerbank/app/views.py
@@ -0,0 +1,42 @@
+"""
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+"""
+
+from .user_mgmt import (register_process,
+ login_process,
+ view_public_accno_process,
+ logout_process)
+from .captcha import (pin_tan_question_attempt,
+ pin_tan_question,
+ pin_tan_verify)
+from .funds_mgmt import (withdraw_process,
+ add_incoming_attempt)
+from .history import (history_process,
+ public_accounts_process)
+from .util import (home_page,
+ profile_process)
+from .test import check_db_test_process
+from django.http import HttpResponse
+from .startup import basic_accounts
+
+
+def ignore(request):
+ return HttpResponse()
+
+
+# create basic accounts if they don't exist
+basic_accounts()
diff --git a/talerbank/settings.py b/talerbank/settings.py
new file mode 100644
index 0000000..a49fa42
--- /dev/null
+++ b/talerbank/settings.py
@@ -0,0 +1,126 @@
+"""
+Django settings for talerbank.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.9/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/1.9/ref/settings/
+"""
+
+import os
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'a2qe1a!d3-y*=!rmivv+-^yzh16b0b1&7@kxid80q0!o1zuv^('
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'talerbank.app'
+]
+
+MIDDLEWARE_CLASSES = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'talerbank.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'talerbank.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.postgresql_psycopg2',
+ 'NAME': 'talerbank',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/1.9/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/1.9/howto/static-files/
+
+STATIC_URL = '/static/'
+
+STATICFILES_DIRS = [
+ os.path.join(BASE_DIR, "app/my-static"),
+]
+
+STATIC_ROOT = 'static/'
diff --git a/talerbank/settings.py.in b/talerbank/settings.py.in
new file mode 100644
index 0000000..d3257cb
--- /dev/null
+++ b/talerbank/settings.py.in
@@ -0,0 +1,126 @@
+"""
+Django settings for talerbank.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.9/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/1.9/ref/settings/
+"""
+
+import os
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'a2qe1a!d3-y*=!rmivv+-^yzh16b0b1&7@kxid80q0!o1zuv^('
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'talerbank.app'
+]
+
+MIDDLEWARE_CLASSES = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'talerbank.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'talerbank.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.postgresql_psycopg2',
+ 'NAME': '@dbnamefinal@',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/1.9/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/1.9/howto/static-files/
+
+STATIC_URL = '/static/'
+
+STATICFILES_DIRS = [
+ os.path.join(BASE_DIR, "app/my-static"),
+]
+
+STATIC_ROOT = 'static/'
diff --git a/talerbank/urls.py b/talerbank/urls.py
new file mode 100644
index 0000000..d40022b
--- /dev/null
+++ b/talerbank/urls.py
@@ -0,0 +1,25 @@
+"""
+Taler Bank URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/1.9/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.conf.urls import url, include
+ 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
+"""
+from django.conf.urls import include, url
+from django.contrib import admin
+from django.conf import settings
+from django.conf.urls.static import static
+
+urlpatterns = [
+ url(r'^', include('talerbank.app.urls')),
+ url(r'^admin/', admin.site.urls),
+] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
diff --git a/talerbank/wsgi.py b/talerbank/wsgi.py
new file mode 100644
index 0000000..6a01acd
--- /dev/null
+++ b/talerbank/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for bank_project project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talerbank.settings")
+
+application = get_wsgi_application()