Skip to content
Snippets Groups Projects
Commit 9bf7a81f authored by Tariq, Usman (PG/T - Comp Sci & Elec Eng)'s avatar Tariq, Usman (PG/T - Comp Sci & Elec Eng)
Browse files

authnetication code

parent 631e966e
No related branches found
No related tags found
2 merge requests!2Pushing the final code to main after testing in QA.,!1Merging the the code to QA for testing
Showing
with 873 additions and 0 deletions
.DS_Store 0 → 100644
File added
LICENSE 0 → 100644
MIT License
Copyright (c) 2024 GloriousGeek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
File added
File added
app.py 0 → 100644
import sqlite3
from flask import Flask, flash, jsonify, redirect, render_template, request, session
from flask_session import Session
from werkzeug.security import generate_password_hash, check_password_hash
from helpers import login_required
# Creating a flask app object
app = Flask(__name__)
app.secret_key="__privatekey__"
# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
@app.after_request
def after_request(response):
"""Ensure responses aren't cached"""
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
con = sqlite3.connect("surreysports.db")
db = con.cursor()
db.execute(
"""
CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name TEXT NOT NULL,
phone_number INTEGER,
email TEXT UNIQUE,
student_id TEXT,
password TEXT NOT NULL,
gender TEXT,
address TEXT
)
"""
)
db.execute(
"""
CREATE TABLE IF NOT EXISTS admin (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name TEXT NOT NULL,
phone_number INTEGER,
email TEXT UNIQUE,
admin_id TEXT,
password TEXT NOT NULL,
gender TEXT,
address TEXT
)
"""
)
# Commiting changes
con.commit()
con.close()
@app.route('/')
@login_required # Decorator func from helpers
def index():
# Check if the user is logged in
if "user_id" in session:
# Retrieve user_id from the session
user_id = session["user_id"]
return render_template('index.html')
@app.route('/signup', methods=['GET', 'POST'])
def signup():
session.clear() # Forget any previous user
con = sqlite3.connect("surreysports.db") # opening db
db = con.cursor() # Obj
if request.method == "POST":
username = request.form.get("student_id")
password = request.form.get("password")
confirm_password = request.form.get("confirm_password")
name = request.form.get("name")
phone_number = request.form.get("phone_no")
email = request.form.get("email")
gender = request.form.get("gender")
address = request.form.get("address")
# Checks for input validation
if not username:
return jsonify({"error": "Student ID is required"}), 400
elif not password:
return jsonify({"error": "Password is required"}), 400
elif not confirm_password:
return jsonify({"error": "Matching Password Confirmation is required"}), 400
elif (password != confirm_password):
return jsonify({"error": "Passwords do not match, please try again"}), 400
# Check if user exists
db.execute("""
SELECT * FROM user
WHERE student_id = ? OR phone_number = ? OR email = ?
""", (username, phone_number, email))
existing_user = db.fetchone()
if existing_user:
return jsonify({"error": "A user with the same username, phone number, or email already exists"}), 400
# Proceed
else:
# Incorporate in the database
db.execute("""
INSERT INTO user (name, student_id, password, phone_number, email, gender, address) VALUES ( ?, ?, ?, ?, ?, ?, ?)""",
(name, username, generate_password_hash(password), phone_number, email, gender, address))
con.commit() # Commit changes to db to save changes.
# Retrieve the user ID after inserting into the database
user_id = db.lastrowid # retrieving id from last inserted row
# Save logged-in user
session["user_id"] = user_id
# Close db connection
con.close()
# Redirect to homepage after registering
return redirect("/")
# For get request
return render_template("signup.html")
# Admin signup flask route
@app.route('/admin/signup', methods=['GET', 'POST'])
def admin_signup():
session.clear() # Forget any previous user
con = sqlite3.connect("surreysports.db") # opening db
db = con.cursor() # Obj
if request.method == "POST":
username = request.form.get("admin_id")
password = request.form.get("password")
confirm_password = request.form.get("confirm_password")
name = request.form.get("name")
phone_number = request.form.get("phone_number")
email = request.form.get("email")
gender = request.form.get("gender")
address = request.form.get("address")
# Checks for input validation
if not username:
return jsonify({"error": "Username is required"}), 400
elif not password:
return jsonify({"error": "Password is required"}), 400
elif not confirm_password:
return jsonify({"error": "Matching Password Confirmation is required"}), 400
elif (password != confirm_password):
return jsonify({"error": "Passwords do not match, please try again"}), 400
# Check if user exists
db.execute("""
SELECT * FROM admin
WHERE admin_id = ? OR phone_number = ? OR email = ?
""", (username, phone_number, email))
existing_admin = db.fetchone()
if existing_admin:
return jsonify({"error": "A user with the same username, phone number, or email already exists"}), 400
# Proceed
else:
# Incorporate in the database
db.execute("""
INSERT INTO admin (name, admin_id, password, phone_number, email, gender, address) VALUES ( ?, ?, ?, ?, ?, ? , ?)""",
(name, username, generate_password_hash(password), phone_number, email, gender, address))
con.commit() # Commit changes to db to save changes.
# Retrieve the user ID after inserting into the database
admin_id = db.lastrowid # retrieving id from last inserted row
# Save logged-in user
session["admin_id"] = admin_id
# Close db connection
con.close()
# Redirect to homepage after registering
return redirect("/")
# For get request
return render_template("signup_admin.html")
@app.route('/login', methods=['GET', 'POST'])
def login():
session.clear() # Forget any user
if request.method == "POST":
login_as = request.form.get("login_as")
username = request.form.get("username")
password = request.form.get("password")
if not login_as or not username or not password:
return jsonify({"error": "Login type, Username and password are required"}), 403
# Select table based on the login_as option
if login_as == "student":
table_name = "user"
id_column = "student_id"
elif login_as == "admin":
table_name = "admin"
id_column = "admin_id"
else:
return jsonify({"error": "Invalid login type"}), 403
con = sqlite3.connect('surreysports.db') # Opening the appropriate database file
db = con.cursor()
# Execute and fetch from result
db.execute(
"""
SELECT * FROM {} WHERE {} = ?
""".format(table_name, id_column),
(username,)
)
row = db.fetchone()
if row and check_password_hash(row[5], password): # removed (and 'password' in row)
session["user_id"] = row[0]
con.close()
return redirect("/")
else:
con.close()
return jsonify({"error": "Invalid username and/or password"}), 403
# For GET requests
return render_template("login.html")
@app.route('/change_password', methods = ['GET', 'POST'])
@login_required
def change_password():
if request.method == "POST":
current_password = request.form.get("current_password")
new_password = request.form.get("new_password")
confirm_password = request.form.get("confirm_password")
# Checks if the fields are empty
if not current_password or not new_password or not confirm_password:
return jsonify({"error": "Fill out all fields"}), 400
# if passwords are not same
if new_password != confirm_password:
return jsonify({"error": "Passwords do not match"}), 400
# Logic to trace the user - user or admin
if "user_id" in session:
table_name = "user"
id_column = "id"
user_id = session["user_id"]
elif "admin_id" in session:
table_name = "admin"
id_column = "id"
user_id = session["admin_id"]
# Check db for current_password
con = sqlite3.connect("surreysports.db")
db = con.cursor()
# db.execute(f"""
# SELECT password FROM {table_name} WHERE {id_column} = ?
# """, (user_id,))
db.execute(
"""
SELECT password FROM {} WHERE {} = ?
""".format(table_name, id_column), (user_id,))
row = db.fetchone()
if not check_password_hash(row[0], current_password):
con.close()
return jsonify({"error": "Current password is incorrect"}), 403
# Updating new password in the appropriate table
# db.execute(f"""
# UPDATE {table_name} SET password = ? WHERE {id_column} = ?
# """, (generate_password_hash(current_password), user_id))
db.execute(
"""
UPDATE {} SET password = ? WHERE {} = ?
""".format(table_name, id_column),
(generate_password_hash(current_password), user_id)
)
con.commit() # Saving changes
con.close()
flash("Password has been updated successfully", "success") # Function in layout.html
return redirect('/')
# return jsonify({"success": "Password has been updated", "redirect": "/"}) # Trying to returns here
return render_template("change_password.html")
@app.route('/logout')
def logout():
"""Log user out"""
# Forget any user_id
session.clear()
return render_template("logout.html")
if __name__ == '__main__':
app.run(debug=True)
import sqlite3
from flask import Flask, jsonify, redirect, render_template, request, session
from flask_session import Session
from werkzeug.security import generate_password_hash, check_password_hash
from helpers import login_required
# Creating a flask app object
app = Flask(__name__)
app.secret_key="__privatekey__"
# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
@app.after_request
def after_request(response):
"""Ensure responses aren't cached"""
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
con = sqlite3.connect("user.db")
db = con.cursor()
db.execute(
"""
CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name TEXT NOT NULL,
phone_number INTEGER,
email TEXT UNIQUE,
student_id TEXT,
password TEXT NOT NULL,
gender TEXT CHECK(gender IN ('male','female','non binary','prefer not to say','other')),
address TEXT
)
"""
)
db.execute(
"""
CREATE TABLE IF NOT EXISTS admin (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name TEXT NOT NULL,
phone_number INTEGER NOT NULL,
email TEXT UNIQUE,
admin_id TEXT,
password TEXT NOT NULL,
gender TEXT CHECK(gender IN ('male','female','non binary','prefer not to say','other')),
address TEXT
)
"""
)
# Commiting changes
con.commit()
con.close()
@app.route('/')
@login_required # Decorator func from helpers
def index():
# Check if the user is logged in
if "user_id" in session:
# Retrieve user_id from the session
user_id = session["user_id"]
return render_template('index.html')
@app.route('/signup', methods=['GET', 'POST'])
def signup():
session.clear() # Forget any previous user
con = sqlite3.connect("user.db") # opening db
db = con.cursor() # Obj
if request.method == "POST":
username = request.form.get("student_id")
password = request.form.get("password")
confirm_password = request.form.get("confirm_password")
name = request.form.get("name")
phone_number = request.form.get("phone_no")
email = request.form.get("email")
address = request.form.get("address")
# Checks for input validation
if not username:
return jsonify({"error": "Student ID is required"}), 400
elif not password:
return jsonify({"error": "Password is required"}), 400
elif not confirm_password:
return jsonify({"error": "Matching Password Confirmation is required"}), 400
elif (password != confirm_password):
return jsonify({"error": "Passwords do not match, please try again"}), 400
# Check if user exists
db.execute("""
SELECT * FROM user
WHERE student_id = ? OR phone_number = ? OR email = ?
""", (username, phone_number, email))
existing_user = db.fetchone()
if existing_user:
return jsonify({"error": "A user with the same username, phone number, or email already exists"}), 400
# Proceed
else:
# Incorporate in the database
db.execute("""
INSERT INTO user (name, student_id, password, phone_number, email, address) VALUES (?, ?, ?, ?, ?, ?)""",
(name, username, generate_password_hash(password), phone_number, email, address))
con.commit() # Commit changes to db to save changes.
# Retrieve the user ID after inserting into the database
user_id = db.lastrowid # retrieving id from last inserted row
# Save logged-in user
session["user_id"] = user_id
# Close db connection
con.close()
# Redirect to homepage after registering
return redirect("/")
# For get request
return render_template("signup.html")
# Admin signup flask route
@app.route('/admin/signup', methods=['GET', 'POST'])
def admin_signup():
session.clear() # Forget any previous user
con = sqlite3.connect("admin.db") # opening db
db = con.cursor() # Obj
if request.method == "POST":
username = request.form.get("admin_id")
password = request.form.get("password")
confirm_password = request.form.get("confirm_password")
name = request.form.get("name")
phone_number = request.form.get("phone_no")
email = request.form.get("email")
address = request.form.get("address")
# Checks for input validation
if not username:
return jsonify({"error": "Username is required"}), 400
elif not password:
return jsonify({"error": "Password is required"}), 400
elif not confirm_password:
return jsonify({"error": "Matching Password Confirmation is required"}), 400
elif (password != confirm_password):
return jsonify({"error": "Passwords do not match, please try again"}), 400
# Check if user exists
db.execute("""
SELECT * FROM admin
WHERE admin_id = ? OR phone_number = ? OR email = ?
""", (username, phone_number, email))
existing_admin = db.fetchone()
if existing_admin:
return jsonify({"error": "A user with the same username, phone number, or email already exists"}), 400
# Proceed
else:
# Incorporate in the database
db.execute("""
INSERT INTO admin (name, admin_id, password, phone_number, email, address) VALUES (?, ?, ?, ?, ? , ?)""",
(name, username, generate_password_hash(password), phone_number, email, address))
con.commit() # Commit changes to db to save changes.
# Retrieve the user ID after inserting into the database
admin_id = db.lastrowid # retrieving id from last inserted row
# Save logged-in user
session["admin_id"] = admin_id
# Close db connection
db.close()
# Redirect to homepage after registering
return redirect("/")
# For get request
return render_template("signup_admin.html")
@app.route('/login', methods=['GET', 'POST'])
def login():
session.clear() # Forget any user
if request.method == "POST":
login_as = request.form.get("login_as")
username = request.form.get("username")
password = request.form.get("password")
if not login_as or not username or not password:
return jsonify({"error": "Login type, Username and password are required"}), 403
# Select table based on the login_as option
if login_as == "student":
table_name = "user"
db_file = "user.db"
id_column = "student_id"
elif login_as == "admin":
table_name = "admin"
db_file = "admin.db"
id_column = "admin_id"
else:
return jsonify({"error": "Invalid login type"}), 403
con = sqlite3.connect(db_file) # Opening the appropriate database file
db = con.cursor()
# Debug print for SQL query and parameters
query = f"SELECT * FROM {table_name} WHERE {id_column} = ?"
print("SQL Query:", query)
print("Parameters:", (username,))
# Execute and fetch from result
db.execute(query, (username,))
row = db.fetchone()
# # Execute and fetch from result
# db.execute(f"SELECT * FROM {table_name} WHERE {id_column} = ?", (username,))
# row = db.fetchone()
print("Row from database:", row) # Debug print
if row:
if row:
hashed_password_from_db = row[5] # Assuming password is the 6th column
print("Hashed password from database:", hashed_password_from_db) # Debug print
print("Hashed password generated during login attempt:", generate_password_hash(password)) # Debug print
if check_password_hash(row[5], password): # Using indexes instead of names coz fetchone returns tuple not dict
# if row and check_password_hash(row[5], password):
session["user_id"] = row[0]
con.close() # Close the database connection
return redirect("/")
con.close()
return jsonify({"error": "Invalid username and/or password"}), 403
# For GET requests
return render_template("login.html")
@app.route('/logout')
def logout():
"""Log user out"""
# Forget any user_id
session.clear()
return render_template("logout.html")
if __name__ == '__main__':
app.run(debug=True)
File added
File added
File added
help.md 0 → 100644
# SQLite3
## fetchone() method:
The fetchone() method returns the first row of the results as a tuple (or None if no row is available), which is useful when you are expecting at most one row to match your query. It's a way of saying, "Give me the next row of results, if there is one."
the execute() method returns a cursor object, on which you can immediately call fetchone()
the execute() function only sends the SQL command to the database; it doesn't actually retrieve the rows.
## commit()
conn.commit() is used to save the changes you make in your database transaction. When you INSERT, UPDATE, or DELETE data in the database, these changes are not immediately saved; they are part of a transaction. By calling conn.commit(), you are telling the database to save all the changes that were made within the transaction. If you don't commit, none of the changes you made during the transaction will be saved to the database.
## db.close()
It's a good practice to close your database connection as soon as you're done with it to free up resources.
## Managing database connections
It's better to manage your database connections on a per-request basis. This ensures that each request has its own connection, which is opened when the request starts and closed when it finishes. In Flask, this is often done with before_request and teardown_request decorators or in the route itself.
\ No newline at end of file
# Using login_required Flask function decorator from the following reference
# https://flask.palletsprojects.com/en/3.0.x/patterns/viewdecorators/
from flask import redirect, render_template, session
from functools import wraps
def login_required(f):
"""
Decorating routes that require login
https://flask.palletsprojects.com/en/3.0.x/patterns/viewdecorators/
"""
@wraps(f)
def decorated_function(*args, **kwargs):
# "user_id" is the identifier associated with currently logged-in user
if session.get("user_id") is None: # Confirm "user_id" in app.py
return redirect("/login")
return f(*args, **kwargs)
return decorated_function
# print(login_required.__doc__)
\ No newline at end of file
static/Database Model.jpg

41 KiB

static/Surrey Sports Park.jpg

845 KiB

static/authentication_logo.png

10.1 KiB

.navbar-top {
background-color:#78c5d7;
height: 20px;
}
.navbar {
background-color:#323f48;
margin-top: 20px;
height: 100px;
}
.logoimg {
height: 75px;
/* Adjust the height as needed */
margin-left: 10px;
/* Adjust the margin as needed */
margin-right: 100px;
}
.nav-link {
color: white;
}
.nav-link:hover {
background-color: #78c5d7;
}
.logout-btn:hover {
background-color: red;
}
.footer {
font-size:10px;
text-align: center;
background-color: #323f48;
padding-top:10px;
color:white;
bottom:0;
position:relative;
width:100%;
}
.footerheading {
font-size: 12px;
}
.logoimgfooter {
height: 55px;
}
.logo {
padding-top:10px;
}
/* .footertext {
color:white;
}
.footer-link {
color:white;
} */
.mainbody {
overflow:auto;
}
body {
min-height: 100vh;
}
File added
__name__
\ No newline at end of file
{% extends "layout.html" %}
{% block title %}
Change Password
{% endblock %}
{% block main %}
<div class="container-fluid">
<div class="row justify-content-center align-items-center min-vh-100">
<div class="col-md-4">
<h2 class="text-center mb-4">Change Password</h2>
<form action="/change_password" method="post">
<div class="mb-3">
<input class="form-control" id="current_password" name="current_password" placeholder="Current Password" type="password">
</div>
<div class="mb-3">
<input class="form-control" id="new_password" name="new_password" placeholder="New Password" type="password">
</div>
<div class="mb-3">
<input class="form-control" id="confirm_password" name="confirm_password" placeholder="Confirm New Password" type="password">
</div>
<button class="btn btn-primary btn-block" type="submit">Change Password</button>
</form>
</div>
</div>
</div>
{% endblock %}
\ No newline at end of file
{% extends "layout.html" %}
{% block title %}
Home
{% endblock %}
{% block main %}
<div class="section">
<h2 class="text-center"><bold>Welcome to Surrey Sports</bold></h2>
</div>
{% endblock %}
<!-- layout.html is the base template that will be extended in other html files for better design and no repeation -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, width=device-width">
<!-- http://getbootstrap.com/docs/5.1/ -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<!-- Bootsrap popper -->
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script>
<!--CSS link -->
<link rel="stylesheet" href="{{ url_for('static', filename='authentication_style.css') }}">
<title>Surrey Sports {% block title %}{% endblock %}</title>
</head>
<body>
{% if get_flashed_messages() %}
<header>
<div class="alert alert-primary mb-0 text-center" role="alert">
{{ get_flashed_messages() | join(" ") }}
</div>
</header>
{% endif %}
<!-- Navigation Top bar -->
<nav class="navbar-top navbar-expand-md bg-custom fixed-top">
<div class="container-fluid">
</div>
</nav>
<!-- Navigation bar -->
<nav class="navbar navbar-expand-md bg-custom">
<div class="container-fluid">
<a href="/" class="logo"><img class="logoimg" src="/static/authentication_logo.png" alt="logo"></a>
{% if session["user_id"] %}
<ul class="navbar-nav me-auto mt-2">
<li class="nav-item"><a class="nav-link btn" href="/sports">Sports</a></li>
<li class="nav-item"><a class="nav-link btn" href="/Booking">Booking</a></li>
<li class="nav-item"><a class="nav-link btn" href="/pitches">Pitches</a></li>
</ul>
<ul class="navbar-nav ms-auto mt-2">
<li class="nav-item"><a class="nav-link btn" href="/change_password">Change Password</a></li>
<li class="nav-item"><a class="nav-link btn btn-danger logout-btn" href="/logout">Log Out</a></li>
</ul>
{% else %}
<ul class="navbar-nav ms-auto mt-2">
<li class="nav-item"><a class="nav-link btn" href="/signup">Register</a></li>
<li class="nav-item"><a class="nav-link btn" href="/login">Log In</a></li>
</ul>
{% endif %}
</div>
</nav>
<!-- our main body -->
<main class="container-fluid py-5 text-center mainbody">
{% block main %}{% endblock %}
</main>
<footer class="footer fixed-bottom">
<div class="container">
<div class="row">
<!-- Useful Links Column -->
<div class="col-md-4">
<h5 class="mb-3 footerheading">Useful Links</h5>
<ul class="list-unstyled">
<li><a class="nav-link btn" href="/sports">Sports</a></li>
<li><a class="nav-link btn" href="/booking">Booking</a></li>
<li><a class="nav-link btn" href="/pitches">Pitches</a></li>
</ul>
</div>
<!-- Register Column -->
<div class="col-md-4">
<h5 class="mb-3 footerheading">Register</h5>
<ul class="list-unstyled">
<li><a class="nav-link btn" href="/change_password">Change Password</a></li>
<li><a class="nav-link btn" href="/signup">Register</a></li>
<li><a class="nav-link btn" href="/login">Log In</a></li>
</ul>
</div>
<!-- Logo Column -->
<div class="col-md-4">
<a href="/" class="logo"><img class="logoimgfooter" src="{{ url_for('static', filename='authentication_logo.png') }}" alt="logo"></a>
</div>
</div>
</div>
<!-- Copyright -->
<div class="row">
<div class="col">
<p class="copyright footertext">&copy; Surrey Sports All rights reserved</p>
</div>
</div>
</footer>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment