Repository navigation
Expand file tree
/
Copy pathmain.py
More file actions
242 lines (177 loc) · 8.84 KB
/
Copy pathmain.py
File metadata and controls
242 lines (177 loc) · 8.84 KB
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
from flask import Flask, render_template, request, redirect, url_for, session
from flask_mysqldb import MySQL
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import MySQLdb.cursors
import re
import bcrypt
import logging
from flask_mail import Mail, Message
import secrets
app = Flask(__name__, static_folder='static')
# Load local settings (see config.example.py). config.py is gitignored so
# credentials are never committed.
from config import Config
app.config.from_object(Config)
app.secret_key = Config.SECRET_KEY
# Logging
logging.basicConfig(filename='app.log', level=logging.INFO)
# Mail and database
mail = Mail(app)
mysql = MySQL(app)
# Create a limiter object
limiter = Limiter(lambda: request.form.get('username', ''), app=app, default_limits=["10/minute"])
# http://localhost:5000/pythonlogin/ - the following will be our login page, which will use both GET and POST requests
@app.route('/efoodproject', methods=['GET', 'POST'])
@limiter.limit('2 per minute')
def login():
# Output message if something goes wrong...
msg = ''
# Check if "username" and "password" POST requests exist (user submitted form)
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
# Create variables for easy access
username = request.form['username']
password = request.form['password']
# Log the username and password
logging.debug(f"Received login request for username: {username}")
# Check if account exists using MySQL
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('SELECT * FROM accounts WHERE username = %s', (username,))
# Fetch one record and return the result
account = cursor.fetchone()
if account and bcrypt.checkpw(password.encode(), account['password'].encode()):
# Create session data, we can access this data in other routes
session['loggedin'] = True
session['id'] = account['id']
session['username'] = account['username']
# Log the successful login
logging.info(f"User logged in successfully: {username}")
# Redirect to home page
return redirect(url_for('home'))
else:
# Account doesnt exist or username/password incorrect
msg = 'Incorrect username/password!'
# Log the failed login attempt
logging.warning(f"Failed login attempt for username: {username}")
return render_template('index.html', msg=msg)
# http://localhost:5000/pythonlogin/logout - this will be the logout page
@app.route('/efoodproject/logout')
def logout():
# Log the logout event
logging.info(f"User logged out: {session['username']}")
# Remove session data, this will log the user out
session.pop('loggedin', None)
session.pop('id', None)
session.pop('username', None)
# Redirect to login page
return redirect(url_for('login'))
# http://localhost:5000/pythonlogin/register - this will be the registration page, we need to use both GET and POST requests
@app.route('/efoodproject/register', methods=['GET', 'POST'])
def register():
# Output message if something goes wrong...
msg = ''
# Check if "username", "password" and "email" POST requests exist (user submitted form)
if request.method == 'POST' and 'username' in request.form and 'password' in request.form and 'email' in request.form:
# Create variables for easy access
username = request.form['username']
password = request.form['password']
email = request.form['email']
# Log the registration request
logging.debug(f"Received registration request for username: {username}")
# Check if account exists using MySQL
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('SELECT * FROM accounts WHERE username = %s', (username,))
account = cursor.fetchone()
if account:
msg = 'Account already exists!'
# Log the account existing error
logging.warning(f"Account already exists for username: {username}")
elif not re.match(r'[^@]+@[^@]+\.[^@]+', email):
msg = 'Invalid email address!'
# Log the invalid email address
logging.warning(f"Invalid email address for username: {username}")
elif not re.match(r'[A-Za-z0-9]+', username):
msg = 'Username must contain only characters and numbers!'
# Log the invalid username
logging.warning(f"Invalid username for registration: {username}")
elif not username or not password or not email:
msg = 'Please fill out the form!'
# Log the missing form data
logging.warning(f"Missing form data for registration: {username}")
else:
# Hash the password
hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
# Generate activation token
activation_token = secrets.token_hex(16)
# Account doesn't exist, and the form data is valid, so insert the new account into the accounts table
cursor.execute('INSERT INTO accounts (username, password, email, activation_token, is_active) VALUES (%s, %s, %s, %s, %s)', (username, hashed_password, email, activation_token, 0))
mysql.connection.commit()
msg = 'You have successfully registered! Please check your email to activate your account.'
# Log the successful registration
logging.info(f"User registered successfully: {username}")
# Send the activation email
send_activation_email(email, activation_token)
elif request.method == 'POST':
# Form is empty... (no POST data)
msg = 'Please fill out the form!'
# Log the empty form data
logging.warning("Empty form data received for registration")
# Show registration form with message (if any)
return render_template('register.html', msg=msg)
# http://localhost:5000/pythinlogin/home - this will be the home page, only accessible for logged in users
@app.route('/efoodproject/home')
def home():
# Check if the user is logged in
if 'loggedin' in session:
# Log the home page access event
logging.info("User accessed home page")
# User is loggedin show them the home page
return render_template('home.html', username=session['username'])
# User is not loggedin redirect to login page
logging.info("User redirected to login page")
return redirect(url_for('login'))
# http://localhost:5000/pythinlogin/profile - this will be the profile page, only accessible for logged in users
@app.route('/efoodproject/profile')
def profile():
# Check if the user is logged in
if 'loggedin' in session:
# Log the profile page access event
logging.info("User accessed profile page")
# We need all the account info for the user so we can display it on the profile page
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('SELECT * FROM accounts WHERE id = %s', (session['id'],))
account = cursor.fetchone()
# Show the profile page with account info
return render_template('profile.html', account=account)
# User is not logged in redirect to login page
logging.info("User redirected to login page")
return redirect(url_for('login'))
def send_activation_email(email, activation_token):
# Prepare email message
subject = "Account Activation"
sender = app.config['MAIL_USERNAME']
recipient = email
activation_link = f"http://localhost:5000/efoodproject/activate/{activation_token}"
message_body = f"Dear user,\n\nPlease click on the following link to activate your account:\n{activation_link}"
# Create and send email message
with app.app_context():
msg = Message(subject=subject, sender=sender, recipients=[recipient])
msg.body = message_body
mail.send(msg)
@app.route('/efoodproject/activate/<activation_token>')
def activate(activation_token):
# Retrieve the user from the database based on the activation token
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('SELECT * FROM accounts WHERE activation_token = %s', (activation_token,))
account = cursor.fetchone()
if account:
# Activate the user's account by updating the database
cursor.execute('UPDATE accounts SET is_active = 1 WHERE activation_token = %s', (activation_token,))
mysql.connection.commit()
# Display a success message or redirect to a login page
return "Account activated successfully!"
else:
# Display an error message or redirect to an error page
return "Invalid activation token."
if __name__ == '__main__':
app.run()