-
Notifications
You must be signed in to change notification settings - Fork 1
/
quotebook.py
300 lines (275 loc) · 9.97 KB
/
quotebook.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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
#!flask/bin/python
from flask import Flask, flash, jsonify, abort, request
from flask import render_template, redirect
from flask_login import LoginManager, UserMixin, login_required, login_user, logout_user, current_user
from boto3.dynamodb.conditions import Key, Attr
import boto3
import time
import datetime
import json
import hashlib
app = Flask(__name__, static_url_path="")
app.secret_key = '{Uj@wtL=,E5NSWz#;&-zy8!czRoUdlE;rag|Uh(dP$`E]wZ}OGVw)Y]-q#X=(>f'
#Enter AWS Credentials
aws_login = json.loads(open('aws.login').read())
AWS_ACCESS_KEY = aws_login['AWS_ACCESS_KEY']
AWS_SECRET_KEY = aws_login['AWS_SECRET_KEY']
REGION = aws_login['REGION']
# Get the table
dynamodb = boto3.resource('dynamodb', aws_access_key_id=AWS_ACCESS_KEY,
aws_secret_access_key=AWS_SECRET_KEY,
region_name=REGION)
table = dynamodb.Table('Quotebook')
# Flask-login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.session_protection = "strong"
login_manager.login_view = "login"
# Make database, user object, user_loader callback
user_table = dynamodb.Table('Quotebook_Users')
user_list = user_table.scan()['Items']
class User(UserMixin):
def __init__(self, username, first_name, last_name, access_level):
self.username = username
self.first_name = first_name
self.last_name = last_name
self.access_level = access_level
self.authenticated = False;
self.active = False;
def is_authenticated(self):
return self.authenticated
def is_active(self):
return self.active
def is_anonymous(self):
return False
def get_id(self):
return str(self.username)
@login_manager.user_loader
def load_user(username):
user_list = user_table.scan()['Items']
for u in user_list:
if u['Username'] == username:
return User(username, u['First Name'], u['Last Name'], u['Access Level'])
return
# Hash input
def hasher(value):
myhash = hashlib.sha512()
myhash.update(value)
return myhash.hexdigest()
# Redirect to login
@app.route('/')
def home():
return redirect("/login")
# Displays login page
@app.route('/login', methods=['GET', 'POST'])
def login():
# handle password hashing and checking
logout_user()
if request.method == 'POST':
result = request.form
username = hasher(result.get('u'))
password = hasher(result.get('p'))
user_list = user_table.scan()['Items']
for u in user_list:
if u['Username'] == username and u['Password'] == password:
user = User(username, u['First Name'], u['Last Name'], u['Access Level'])
login_user(user)
return redirect('/quotes')
return render_template('login.html', error='Invalid username or password')
else:
return render_template('login.html', error=None)
# Show sorted quotes, who can view, and allow deletion/editing
@app.route('/quotes', methods=['GET'])
@login_required
def quotes_page():
quote_list = table.scan()['Items']
quote_list.sort(key=lambda x: time.mktime(time.strptime(x['Timestamp'], '%Y-%m-%d %H:%M:%S')[0:9]), reverse=True)
user_list = user_table.scan()['Items']
# Search through quotes
for i in range(len(quote_list)):
q = quote_list[i]
custom_list = []
if type(q.get('View')) is list:
# Search through list of allowed users
for allowed_users in q.get('View'):
# Search through user list
for user in user_list:
if user.get('Username') in allowed_users:
custom_list.append(user['First Name'] + " " + user['Last Name'])
quote_list[i]['Viewers'] = custom_list
return render_template('quotebook.html', user=current_user, quote_list=quote_list)
# Add quote
@app.route('/add_quote', methods=['GET', 'POST'])
@login_required
def add_quote():
# Guests may not add quotes
if (current_user.access_level == "Guest"):
return redirect('/quotes')
user_list = user_table.scan()['Items']
if request.method == 'POST':
result = request.form
# get quote
quote = result.get('new_quote')
# get author
author = result.get('author')
if current_user.access_level == "User" or author == None:
author = current_user.first_name + " " + current_user.last_name
elif author == "Other":
author = result.get('custom')
if (not author):
author = "Anonymous"
# get timestamp
ts = time.time()
timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
# get custom views
view = result.get('view')
if view == "custom":
view = result.getlist('username_list')
# publish on database
data = {"Timestamp": timestamp, "Author": author, "Quote": quote, "View": view, "Username": current_user.username}
table.put_item(Item=data)
flash("Your quote was submitted successfully! :)")
return render_template('add_quote.html', user=current_user, user_list=user_list)
else:
return render_template('add_quote.html', user=current_user, user_list=user_list)
# Delete quote
@app.route('/delete_quote/<string:ts>/<string:author>/<string:username>', methods=['GET'])
@login_required
def delete_quote(ts, author, username):
# guests can't delete quotes
if current_user.access_level == "Guest":
return redirect('/quotes')
if (current_user.access_level == "User" and current_user.username == username) or current_user.access_level == "Admin":
try:
table.delete_item(Key={'Timestamp': ts, 'Author': author})
flash("Quote was successfully deleted.")
except:
flash("An error has occurred. Quote was not deleted.")
return redirect('/quotes')
# Edit quote
@app.route('/edit_quote/<string:ts>/<string:author>/<string:username>', methods=['GET', 'POST'])
@login_required
def edit_quote(ts, author, username):
# guests can't edit quotes
if current_user.access_level == "Guest":
return redirect('/quotes')
user_list = user_table.scan()['Items']
if request.method == 'POST':
if (current_user.access_level == "User" and current_user.username == username) or current_user.access_level == "Admin":
try:
if request.method == 'POST':
result = request.form
# get quote
quote = result.get('new_quote')
# get custom views
view = result.get('view')
if view == "custom":
view = result.getlist('username_list')
# publish on database
data = {"Timestamp": ts, "Author": author, "Quote": quote, "View": view, "Username": username}
table.put_item(Item=data)
flash("Quote was successfully edited.")
except:
flash("An error has occurred. Quote was not edited.")
return redirect('/quotes')
else:
quote = table.scan(FilterExpression=Attr('Timestamp').eq(ts)).get('Items')
if len(quote) > 0:
quote = quote[0]
return render_template('edit_quote.html', quote=quote, user=current_user, user_list=user_list)
# Manage accounts
@app.route('/accounts', methods=['GET'])
@login_required
def manage_users():
# only admins can delete accounts
if (not current_user.access_level == "Admin"):
return redirect('/quotes')
user_list = user_table.scan()['Items']
user_list.sort(key=lambda k: k.get('Last Name'))
return render_template('accounts.html', user=current_user, user_list=user_list)
# Add accounts
@app.route('/accounts', methods=['GET', 'POST'])
@login_required
def add_account():
# only admins can add accounts
if (not current_user.access_level == "Admin"):
return redirect('/quotes')
if request.method == 'POST':
result = request.form
username = hasher(result.get('Username'))
password = hasher(result.get('Password'))
access_level = result.get('Access_Level')
first_name = result.get('First_Name')
last_name = result.get('Last_Name')
user_list = user_table.scan().get('Items')
for item in user_list:
if item.get('Username') == username:
flash("Please select a unique username.")
return redirect('/accounts')
new_user = {"Username": username, "Password": password, "Access Level": access_level, "First Name": first_name, "Last Name": last_name}
user_table.put_item(Item=new_user)
try:
flash("User was successfully added.")
except:
flash("Failed to add user.")
return redirect('/accounts')
# Delete account
@app.route('/delete_account/<string:username>/<string:access_level>', methods=['GET', 'POST'])
@login_required
def delete_user(username, access_level):
# only admins can delete accounts
if (not current_user.access_level == "Admin"):
return redirect('/quotes')
if (access_level == "Admin"):
flash("You may not delete Admin accounts")
else:
try:
user_table.delete_item(Key={'Username': username})
flash("Account successfully deleted")
except:
flash("Error when deleting account")
return redirect('/accounts')
# Settings
@app.route('/settings', methods=['GET'])
@login_required
def settings():
# Guests not allowed to access
if current_user.access_level == "Guest":
return redirect('/quotes')
return render_template('settings.html')
# Update Settings
@app.route('/settings', methods=['GET', 'POST'])
@login_required
def update_settings():
# Guests not allowed to access
if current_user.access_level == "Guest":
return redirect('/quotes')
if request.method == 'POST':
result = request.form
old_password = hasher(result.get('Password_Old'))
new_password = hasher(result.get('Password_New'))
user_list = user_table.scan()['Items']
info = (item for item in user_list if item['Username'] == current_user.username).next()
if old_password == info['Password']:
info['Password'] = new_password
user_table.put_item(Item=info)
flash("Password successfully updated!")
else:
flash("The password you typed is incorrect.")
return render_template('settings.html')
# Log out
@app.route('/logout')
def logout():
logout_user()
return redirect('/login')
# 500 Internal server error
@app.errorhandler(500)
def internal_server_error(e):
return render_template("error500.html"), 500
# 404 Not Found
@app.errorhandler(404)
def page_nots_found(e):
return render_template("error404.html"), 404
# Running on EC2 Instance
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=80)