Files
function/routes/auth.py

107 lines
4.8 KiB
Python

from flask import Blueprint, render_template, request, redirect, url_for, flash
from flask_login import login_user, logout_user, login_required, UserMixin, current_user
from werkzeug.security import generate_password_hash, check_password_hash
from extensions import db, login_manager, environment, htmx
from jinja2_fragments import render_block
auth = Blueprint('auth', __name__)
def get_client_ip():
"""Get real client IP address, checking proxy headers first"""
# Check common proxy headers in order of preference
if request.headers.get('X-Forwarded-For'):
# X-Forwarded-For can contain multiple IPs, get the first (original client)
return request.headers.get('X-Forwarded-For').split(',')[0].strip()
elif request.headers.get('X-Real-IP'):
return request.headers.get('X-Real-IP')
elif request.headers.get('CF-Connecting-IP'): # Cloudflare
return request.headers.get('CF-Connecting-IP')
else:
# Fallback to direct connection IP
return request.remote_addr
class User(UserMixin):
def __init__(self, id, username, password_hash, created_at, theme_preference='light', email=None):
self.id = id
self.username = username
self.password_hash = password_hash
self.created_at = created_at
self.theme_preference = theme_preference
self.email = email
@staticmethod
def get(user_id):
user_data = db.get_user(int(user_id))
if user_data:
return User(id=str(user_data['id']), username=user_data['username'], password_hash=user_data['password_hash'], created_at=user_data['created_at'], theme_preference=user_data.get('theme_preference', 'light'), email=user_data.get('email'))
return None
@login_manager.user_loader
def load_user(user_id):
user_data = db.get_user(int(user_id))
if user_data:
return User(id=str(user_data['id']), username=user_data['username'], password_hash=user_data['password_hash'], created_at=user_data['created_at'], theme_preference=user_data.get('theme_preference', 'light'), email=user_data.get('email'))
return None
@auth.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template("login.html")
username = request.form.get('username')
password = request.form.get('password')
if not username or not password:
return render_template("login.html", error="Both username and password must be entered")
user_data = db.get_user_by_username(username)
if not user_data:
# Record failed login attempt with real IP
db.record_login(None, get_client_ip(), str(request.user_agent), False, "User not found")
return render_template("login.html", error="User does not exist")
if not check_password_hash(user_data['password_hash'], password):
# Record failed login attempt with real IP
db.record_login(user_data['id'], get_client_ip(), str(request.user_agent), False, "Invalid password")
return render_template("login.html", error="Invalid username or password")
user = User(id=str(user_data['id']), username=user_data['username'], password_hash=user_data['password_hash'], created_at=user_data['created_at'], theme_preference=user_data.get('theme_preference', 'light'), email=user_data.get('email'))
# Record successful login with real IP
db.record_login(user.id, get_client_ip(), str(request.user_agent), True)
login_user(user)
next = request.args.get('next')
return redirect(next or url_for('home.index'))
@auth.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'GET':
return render_template("signup.html")
username = request.form.get('username')
password = request.form.get('password')
if not username or not password:
return render_template("signup.html", error="Both username and password must be entered")
if len(username) < 10 or len(password) < 10:
return render_template("signup.html", error="Both username and password must be at least 10 characters long")
user = db.get_user_by_username(username)
if user:
return render_template("signup.html", error="User already exists")
hashed_password = generate_password_hash(password)
user_data = db.create_new_user(username, hashed_password)
user = User(id=str(user_data['id']), username=user_data['username'], password_hash=user_data['password_hash'], created_at=user_data['created_at'], theme_preference=user_data.get('theme_preference', 'light'), email=user_data.get('email'))
login_user(user)
return redirect(url_for('home.index'))
@auth.route("/logout")
@login_required
def logout():
logout_user()
return redirect(url_for('landing_page'))