-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.py
273 lines (252 loc) · 12.4 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
from bcrypt import checkpw, gensalt, hashpw
from flask import Flask, abort, render_template, redirect, request, session
from flask_talisman import Talisman
from traceback import print_exc
from bank import Member, BASE_RATES
member_ids = []
try:
for id in Member.cursor.execute('SELECT id FROM members;'):
member_ids.append(id[0])
for id in member_ids:
Member(id)
app = Flask(__name__)
Talisman(app, force_https_permanent=True, frame_options='DENY', content_security_policy={
'default-src': "'self'",
'style-src': ["'self'", "'unsafe-inline'"],
'navigate-to': "'self'",
'form-action': "'self'",
'object-src': "'none'",
'frame-src': "'none'",
'media-src': "'none'",
'connect-src': "'none'"
})
app.secret_key = b'016480fad86b560ddda3776246a9d959c718dab93e2632b8c93e29142ad46a73'
@app.route("/")
def home():
if session.get("id"):
member = Member.get_member(session['id'])
member.sync_balance()
try:
return render_template('home.html', balance=member.balance, accounts=member.accounts)
except AttributeError:
session.pop("id")
return render_template('index.html')
@app.route("/login/", methods=["GET", "POST"])
def login():
wrong = False
if request.method == "POST":
try:
if checkpw(request.form["password"].encode('utf-8'), Member.get_member(request.form['id']).pass_hash()):
session.update({"id": str(request.form["id"])})
return redirect(request.args.get('redir', default='/', type=str), 303)
wrong = True
except (ValueError, AttributeError, TypeError, KeyError):
wrong = True
return render_template('login.html', wrong=wrong, new=False, redir=request.args.get('redir', default='/', type=str))
@app.post("/logout/")
def logout():
session.pop("id", None)
return redirect("/", 303)
@app.route("/sign-up/", methods=['GET', 'POST'])
def sign_up():
if request.method == 'POST':
if request.form['password'] == request.form['confirm-password']:
member = Member(None, request.form['f-name'], request.form['l-name'],
hashpw(request.form['password'].encode('utf-8'), gensalt()))
Member(member.id)
return render_template("login.html", wrong=False, new=member.id, redir='/')
return render_template('sign-up.html', match_issue=True)
return render_template('sign-up.html', match_issue=False)
@app.route("/direct-transfer/", methods=['GET', 'POST'])
def direct_transfer():
if session.get('id'):
exists = True
charged = ""
remaining = ""
if request.method == 'POST':
try:
if float(request.form['amount']) < 1:
return '', 400
charged, remaining, _ = Member.get_member(session['id']).charge(Member.get_member(
str(request.form['to'])), int(float(request.form['amount']) * 100), False)
except AttributeError:
exists = False
return render_template('direct-transfer.html', exists=exists, charged=charged, remaining=remaining)
return redirect("/login/?redir=/direct-transfer/", 303)
@app.route("/open-account/", methods=['GET', 'POST'])
def open_account():
if session.get('id'):
done = False
poor = False
if request.method == 'POST':
try:
if request.form['type'] == 'savings':
done = Member.get_member(session['id']).create_account(
int(float(request.form["starting-amount"]) * 100), Member.get_member(session['id']).calc_r('savings'))
elif request.form['type'] == 'cd':
done = Member.get_member(session['id']).create_account(
int(float(request.form["starting-amount"]) * 100), Member.get_member(session['id']).calc_r('cd', float(request.form['term'])), float(request.form['term']))
elif request.form['type'] == 'checking':
done = Member.get_member(session['id']).create_account(
int(float(request.form["starting-amount"]) * 100))
poor = False
except ValueError:
done = False
poor = True
rates = {-1: Member.get_member(session['id']).calc_r('savings')}
for term in BASE_RATES['cd']:
rates.update({float(term): Member.get_member(
session['id']).calc_r('cd', float(term))})
return render_template('open-account.html', rates=rates, done=done, poor=poor)
return redirect("/login/?redir=/open-account/", 303)
@app.route("/get-loan/", methods=['GET', 'POST'])
def get_loan():
if session.get('id'):
if request.method == 'POST':
# TODO
pass
return render_template('get-loan.html', rate=Member.get_member(session['id']).calc_r('loan'))
return redirect("/login/?redir=/get-loan/", 303)
@app.route("/accounts/<int:id>/", methods=['GET', 'POST'])
@app.route("/accounts/")
def accounts(id=None):
if session.get('id'):
member = Member.get_member(session['id'])
# Page displaying all accounts
if id is None:
return(render_template("accounts.html", accounts=member.accounts))
else:
# Page with more info on one account
if request.method == 'GET':
account = [a for a in member.accounts if a.id == id][0]
return render_template("account.html", account=account, checking=True if account is member.checking else False, accounts=member.accounts)
if request.form.get('debit') == True:
pass
return redirect("/login/?redir=/accounts/", 303)
@app.route("/accounts/close/<int:id>/", methods=['GET', 'POST'])
def close_account(id: int):
if session.get('id'):
accounts = Member.get_member(session['id']).accounts
account = None
for account in accounts:
if account.id == id:
break
if account is None:
return "Whoops, that account doesn't exist", 404
if account.balance > 0:
return redirect("/accounts/transfer/" + str(id) + "/", 303)
else:
accounts.remove(account)
Member.get_member(session['id']).sync_balance
Member.get_member(session['id']).save()
return redirect("/accounts/", 303)
return redirect("/login/?redir=/accounts/close-account/" + str(id) + "/", 303)
@app.route("/accounts/transfer/<int:id>/", methods=['GET', 'POST'])
def transfer(id: int):
"""Not to be confused with direct transfer, this is for
transferring from one of one's accounts to another, not from
one's checking to another's.
"""
if session.get('id'):
for account in Member.get_member(session['id']).accounts:
if account.id == id:
from_ = account
break
# Invalid `id`
elif account is Member.get_member(session['id']).accounts[-1]:
abort(400)
if request.method == 'GET':
theirs = False
accounts = Member.get_member(session['id']).accounts.copy()
for account in Member.get_member(session['id']).accounts:
if account.id == id:
theirs = True
accounts.remove(account)
break
if not theirs:
return "Whoops, that account doesn't exist", 404
Member.get_member(session['id']).sync_balance
Member.get_member(session['id']).save()
return render_template("transfer.html", account=account, accounts=accounts, funds=from_.balance)
else:
for account in Member.get_member(session['id']).accounts:
if account.id == int(request.form['to']):
to = account
break
if from_.balance >= int(float(request.form['amount']) * 100):
from_.balance -= int(float(request.form['amount']) * 100)
else:
abort(400)
to.balance += int(float(request.form['amount']) * 100)
return redirect("/accounts/" + str(id) + "/", 303)
return redirect("/login/?redir=/accounts/transfer/" + str(id) + "/", 303)
@app.route("/accounts/manage-debit-access/<_>/", methods=['GET', 'POST'])
def manage_debit_access(_=None):
if _ is not None:
redirect("/accounts/manage-debit-access/", 308)
if session.get('id'):
if request.method == 'POST':
# Setting/changing PIN
if request.form.get('turn-on') == 'on':
pin_hash = hashpw(request.form['pin'].encode('utf-8'), gensalt())
Member.get_member(session['id']).pin_hash = lambda: pin_hash
# Turning off access
else:
Member.get_member(session['id']).pin_hash = lambda: None
Member.get_member(session['id']).save()
return redirect("/accounts/" + str(Member.get_member(session['id']).checking.id) + "/", 303)
return render_template("debit.html", checked=bool(Member.get_member(session['id']).pin_hash()))
return redirect("/login/?redir=/accounts/manage-debit-access/", 303)
@app.route("/transaction-history/")
def transaction_history():
if session.get('id'):
return render_template("transactions.html", transactions=Member.get_member(session['id']).transactions, get_member=Member.get_member)
return redirect("/login/?redir=/transaction-history/", 303)
@app.route("/get-credit-card/")
def get_credit_card():
# TODO
abort(503, "Under Development")
@app.post("/charge/")
def charge():
# If the request doesn't say how long it is
if request.content_length is None:
return "", 411
# If the request is waaay too long
elif request.content_length > 200:
return "", 413
try:
# If the request if from an unregistered IP
if request.remote_addr not in [ip[0] for ip in Member.cursor.execute('SELECT ip FROM registered_ips')]:
return "", 403
if request.json['type'] == 'credit':
return "Under Development", 501
elif request.json['type'] == 'debit':
# If the account is not debit enabled
if Member.get_member(str(request.json['payer-id'])).pin_hash() is None:
return "", 404
if float(request.json["amount"]) < 1:
return "", 400
# If all goes well
if checkpw(str(request.json["pin"]).encode('utf-8'), Member.get_member(str(request.json['payer-id'])).pin_hash()):
Member.get_member(str(request.json['payer-id'])).sync_balance()
Member.get_member(str(request.json['payer-id'])).save()
Member.get_member(str(request.json['recipient-id'])).sync_balance()
Member.get_member(str(request.json['recipient-id'])).save()
response = list(Member.get_member(request.json["payer-id"]).charge(Member.get_member(str(request.json["recipient-id"])), float(request.json["amount"]), bool(request.json["taxable"])))
code = 205 if response[1] < 0 else 201
return response, code
# If PIN is incorrect
return "", 401
return "", 400
# If some Member didn't exist
except (TypeError, AttributeError, ValueError) as e:
return "", 404
# If anything else goes wrong, blame it on the client
except:
return "", 400
if __name__ == '__main__':
app.run('0.0.0.0', 443, ssl_context='adhoc')
except:
print_exc()
Member.close()
raise SystemExit