diff --git a/app.py b/app.py index a4921ba..2951448 100644 --- a/app.py +++ b/app.py @@ -1,12 +1,30 @@ import datetime +import hashlib from flask import Flask, render_template, redirect, url_for, request -from flask_login import login_required, LoginManager +from flask_login import login_required, LoginManager, login_user, logout_user, current_user from models.User import User +from utils import anonymous_required # Create a new Flask instance app = Flask(__name__) +app.secret_key = 'PSSSSSHHHT!' + +# Initialize the Flask-Login extension +login_manager = LoginManager() +login_manager.login_view = 'login' +login_manager.init_app(app) + + +@login_manager.user_loader +def load_user(user_id): + return User.get(user_id) + + +@app.context_processor +def inject_user(): + return dict(user=current_user) # Create a new route @@ -15,20 +33,25 @@ def index(): # return 'Hello World' return render_template('index.html', title='Home', utc_dt=datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S")) + @app.route('/test') @login_required def secret(): return 'Pssst!' + @app.route('/login') +@anonymous_required def login(): - return render_template('auth/login.html') + return render_template('auth/login.html', errors={}) @app.route('/signup') +@anonymous_required def signup(): return render_template('auth/signup.html', errors={}) + @app.route('/signup', methods=['POST']) def signup_post(): email = request.form.get('email') @@ -44,36 +67,66 @@ def signup_post(): if not password: errors['password'] = 'Password is required.' - return render_template( - 'auth/signup.html', - email=email, - name=name, - password=password, - errors=errors - ) + if errors: + return render_template( + 'auth/signup.html', + email=email, + name=name, + password=password, + errors=errors + ) # Save user to database. Maybe log the user in directly. + user = User.create(name, email, password) + login_user(user) # Redirect to login page - return redirect(url_for('login')) + return redirect(url_for('secret')) + + +@app.route('/login', methods=['POST']) +def login_post(): + email = request.form.get('email') + password = request.form.get('password') + + # Check for errors + errors = {} + if not email: + errors['email'] = 'Email is required.' + if not password: + errors['password'] = 'Password is required.' + + # Check if user exists + user = User.get_by_email(email) + + if not user: + errors['email'] = 'User does not exist.' + elif user.password is None or hashlib.sha256(password.encode()).hexdigest() != user.password: + errors['password'] = 'Password incorrect.' + + if errors: + return render_template( + 'auth/login.html', + email=email, + password=password, + errors=errors + ) + + login_user(user) + + # Redirect to login page + return redirect(url_for('secret')) @app.route('/logout') @login_required def logout(): # Log out functionality + logout_user() + return redirect(url_for('index')) # Run the application if __name__ == '__main__': - login_manager = LoginManager() - login_manager.login_view = 'login' - login_manager.init_app(app) - app.secret_key = 'PSSSSSHHHT!' - - @login_manager.user_loader - def load_user(user_id): - return User.get(user_id) - app.run(port=5000, debug=True) diff --git a/db/SQLiteClient.py b/db/SQLiteClient.py index 4fa6c8c..03aa513 100644 --- a/db/SQLiteClient.py +++ b/db/SQLiteClient.py @@ -1,3 +1,4 @@ +from datetime import datetime import hashlib import sqlite3 @@ -7,9 +8,11 @@ def con3(): return conn -def create_user(name, email, password): - password = hashlib.md5(password.encode()).hexdigest() - query = f"INSERT INTO users (name, email, password) VALUES ({name}, {email}, {password})" +def create_user(name: str, email: str, password: str): + password = hashlib.sha256(password.encode()).hexdigest() + now = datetime.now().isoformat() + query = (f"INSERT INTO users (name, email, password, created_at, updated_at) VALUES ('{name}', '{email}', " + f"'{password}', '{now}', '{now}');") conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -18,8 +21,18 @@ def create_user(name, email, password): return cursor.lastrowid -def get_user(id): - query = f"SELECT * FROM users WHERE id = {id}" +def get_user(id: int): + query = f"SELECT * FROM users WHERE id = {id};" + conn = con3() + cursor = conn.cursor() + cursor.execute(query) + user = cursor.fetchone() + conn.close() + return user + + +def get_user_by_email(email: str): + query = f"SELECT * FROM users WHERE email = '{email}';" conn = con3() cursor = conn.cursor() cursor.execute(query) diff --git a/db/db.sqlite b/db/db.sqlite deleted file mode 100644 index 5602513..0000000 Binary files a/db/db.sqlite and /dev/null differ diff --git a/models/User.py b/models/User.py index 466b112..0a1acbe 100644 --- a/models/User.py +++ b/models/User.py @@ -1,18 +1,26 @@ -from db.SQLiteClient import create_user, get_user +from flask_login import UserMixin + +from db.SQLiteClient import create_user, get_user, get_user_by_email -class User: - def __init__(self, id, name, email): +class User(UserMixin): + def __init__(self, id, name, email, password=None): self.id = id self.name = name self.email = email + self.password = password @staticmethod - def create(name, email, password): + def create(name: str, email: str, password: str): id = create_user(name, email, password) return User(id, name, email) @staticmethod - def get(id): + def get(id: int): user = get_user(id) - return User(user[0], user[1], user[2]) if user else None \ No newline at end of file + return User(user[0], user[1], user[2], user[3]) if user else None + + @staticmethod + def get_by_email(email: str): + user = get_user_by_email(email) + return User(user[0], user[1], user[2], user[3]) if user else None diff --git a/templates/auth/login.html b/templates/auth/login.html index c4af8fa..68edeca 100644 --- a/templates/auth/login.html +++ b/templates/auth/login.html @@ -8,19 +8,25 @@
- + +
+ {{ errors.get('email', '') }} +
- + +
+ {{ errors.get('password', '') }} +
- +
diff --git a/templates/layouts/main.html b/templates/layouts/main.html index 6667d19..a888ced 100644 --- a/templates/layouts/main.html +++ b/templates/layouts/main.html @@ -36,12 +36,18 @@ diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..98f3494 --- /dev/null +++ b/utils.py @@ -0,0 +1,14 @@ +from functools import wraps + +from flask import redirect, url_for +from flask_login import current_user + + +def anonymous_required(f): + @wraps(f) + def decorated_function(*args, **kwargs): + if current_user.is_authenticated: + return redirect(url_for('index')) + return f(*args, **kwargs) + + return decorated_function