From 05ffc692e0d4783cb7aaaeb7751046aae077e863 Mon Sep 17 00:00:00 2001
From: "Ethan Clay (UWE)" <ethan2.clay@live.uwe.ac.uk>
Date: Tue, 10 Dec 2024 11:05:11 +0000
Subject: [PATCH] Begin to work on authentication for users

---
 app/__init__.py                  | 39 ++++++++++++++++++----------
 app/auth/__init__.py             |  5 ++++
 app/auth/routes.py               | 44 ++++++++++++++++++++++++++++++++
 app/models/user.py               |  6 ++---
 app/profile/routes.py            | 20 ++++++++++-----
 app/templates/profile/index.html |  3 ++-
 6 files changed, 92 insertions(+), 25 deletions(-)
 create mode 100644 app/auth/__init__.py
 create mode 100644 app/auth/routes.py

diff --git a/app/__init__.py b/app/__init__.py
index df1d438..a8bae28 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -4,11 +4,13 @@ from flask import Flask
 from config import Config
 from flask_sqlalchemy import SQLAlchemy
 from flask_migrate import Migrate
+from flask_login import LoginManager
 from dotenv import load_dotenv
 import os
 
 db = SQLAlchemy()
 migrate = Migrate()
+login_manager = LoginManager()
 
 def create_app(config_class=Config):    
     app = Flask(__name__)
@@ -36,22 +38,31 @@ def create_app(config_class=Config):
     migrate.init_app(app, db)
 
     # Register blueprints and url prefixes
-    from app.main import bp as main_bp
-    app.register_blueprint(main_bp)
+    register_blueprints(app)
     
-    from app.bookings import bp as bookings_bp
-    app.register_blueprint(bookings_bp, url_prefix='/bookings')
-    
-    from app.api import bp as api_bp
-    app.register_blueprint(api_bp, url_prefix='/api')
-    
-    from app.admin import bp as admin_bp
-    app.register_blueprint(admin_bp, url_prefix='/admin')
-    
-    from app.profile import bp as profile_bp
-    app.register_blueprint(profile_bp, url_prefix='/profile')
     
     if __name__ == "__main__":
         app.run(use_reloader=True)
+        
+    login_manager.login_view = 'profile.login'
+    login_manager.init_app(app)
+
+    return app
+
+@login_manager.user_loader
+def load_user(user_id):
+    from .models import User
+    return User.query.get(int(user_id))
 
-    return app
\ No newline at end of file
+def register_blueprints(app):
+    blueprints = [
+        ('auth', None),
+        ('main', None),
+        ('bookings', '/bookings'),
+        ('api', '/api'),
+        ('admin', '/admin'),
+        ('profile', '/profile')
+    ]
+    for module_name, url_prefix in blueprints:
+        module = __import__(f'app.{module_name}', fromlist=['bp'])
+        app.register_blueprint(module.bp, url_prefix=url_prefix)
\ No newline at end of file
diff --git a/app/auth/__init__.py b/app/auth/__init__.py
new file mode 100644
index 0000000..2834ca1
--- /dev/null
+++ b/app/auth/__init__.py
@@ -0,0 +1,5 @@
+from flask import Blueprint
+
+bp = Blueprint('auth', __name__)
+
+from app.auth import routes
\ No newline at end of file
diff --git a/app/auth/routes.py b/app/auth/routes.py
new file mode 100644
index 0000000..fa6a1b6
--- /dev/null
+++ b/app/auth/routes.py
@@ -0,0 +1,44 @@
+#https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login#step-1-installing-packages
+from flask import Blueprint, render_template, redirect, url_for, request
+from app.auth import bp
+from werkzeug.security import generate_password_hash, check_password_hash
+from app.models import User
+from app import db
+from flask_login import login_user
+
+@bp.route('/signup')
+def signup():
+    return render_template(url_for('profile.signup'))
+
+@bp.route('/signup', methods=['POST'])
+def signup_post():
+    email = request.form.get('email')
+    username = request.form.get('name')
+    password = request.form.get('password')
+
+    user = User.query.filter_by(email=email).first()
+
+    if user:
+        return redirect(url_for('profile.signup'))
+
+    new_user = User(username=username, email=email, password=generate_password_hash(password, method='pbkdf2:sha256'), role_id=2)  # Assuming role_id is required and you have a default value or retrieve it from elsewhere
+
+    db.session.add(new_user)
+    db.session.commit()
+
+    return redirect(url_for('profile.login'))
+
+@bp.route('/login', methods=['POST'])
+def login_post():
+    email = request.form.get('email')
+    password = request.form.get('password')
+    remember = True if request.form.get('remember') else False # If checkbox for user is ticked
+
+    user = User.query.filter_by(email=email).first()
+
+    if not user or not check_password_hash(user.password, password):
+        flash('Please check your login details and try again.')
+        return redirect(url_for('profile.login'))
+
+    login_user(user, remember=remember)
+    return redirect(url_for('main.profile'))
\ No newline at end of file
diff --git a/app/models/user.py b/app/models/user.py
index c1881bf..8431368 100644
--- a/app/models/user.py
+++ b/app/models/user.py
@@ -1,7 +1,8 @@
 from flask import request, jsonify
+from flask_login import UserMixin
 from app import db
 
-class User(db.Model):
+class User(UserMixin, db.Model):
     __tablename__ = 'users'
 
     id = db.Column(db.Integer, primary_key=True)
@@ -17,16 +18,15 @@ class User(db.Model):
         new_user = cls(username=username, email=email, password=password, role_id=role_id)
         db.session.add(new_user)
         db.session.commit()
+        
         return new_user
 
     @classmethod
     def search_user_id(cls, user_id):
         return cls.query.get(user_id)
     
-
     @classmethod
     def search_user_by_email(cls, user_email):
-        
         user_exist = cls.query.filter_by(email=user_email).first()
         
         return user_exist
\ No newline at end of file
diff --git a/app/profile/routes.py b/app/profile/routes.py
index 2fd51c3..f827e4e 100644
--- a/app/profile/routes.py
+++ b/app/profile/routes.py
@@ -1,33 +1,39 @@
 from flask import render_template, redirect, url_for
 from app.profile import bp
 from app.models import User 
+from flask_login import current_user, login_required
 
 @bp.route('/')
+@login_required
 def index():
-    return render_template('profile/index.html')
+    if current_user.is_authenticated:
+        return render_template('profile/index.html', username=current_user.username)
+    else:
+        return redirect(url_for('profile.login'))
 
 @bp.route('/login')
 def login():
     return 'Login'
 
-@bp.route('/signup')
-def signup():
-    return render_template('profile/signup.html')
-
 @bp.route('/signup', methods=['POST'])
 def signup_post():
+    return 'hit POST'
     email = request.form.get('email')
     name = request.form.get('name')
     password = request.form.get('password')
     if User.search_user_by_email(email):
-        return redirect(url_for('auth.signup'))
+        return redirect('signup.html')
 
     new_user = User(email=email, name=name, password=generate_password_hash(password, method='sha256'))
 
     db.session.add(new_user)
     db.session.commit()
     
-    return redirect(url_for('auth.login'))
+    return redirect('profile/login.html')
+
+@bp.route('/signup')
+def signup():
+    return render_template('profile/signup.html')
 
 @bp.route('/logout')
 def logout():
diff --git a/app/templates/profile/index.html b/app/templates/profile/index.html
index 7da6f97..f899369 100644
--- a/app/templates/profile/index.html
+++ b/app/templates/profile/index.html
@@ -2,6 +2,7 @@
 # Implements CSS Slider from https://swiffyslider.com/docs/
 {% block content %}
 <div>
-    <p>Welcome logged in!</p>
+    <p>Welcome {{username}}!</p>
+    
 </div>
 {% endblock %}
\ No newline at end of file
-- 
GitLab