Move auth logic to blueprint
This commit is contained in:
91
app.py
91
app.py
@@ -4,9 +4,9 @@ from flask import Flask, Response, jsonify, redirect, render_template, render_te
|
|||||||
import jinja_partials
|
import jinja_partials
|
||||||
from jinja2_fragments import render_block
|
from jinja2_fragments import render_block
|
||||||
import requests
|
import requests
|
||||||
from extensions import db, htmx, init_app
|
from extensions import db, htmx, init_app, login_manager
|
||||||
from services import create_http_function_view_model, create_http_functions_view_model
|
from services import create_http_function_view_model, create_http_functions_view_model
|
||||||
from flask_login import LoginManager, UserMixin, current_user, login_required, login_user, logout_user
|
from flask_login import current_user, login_required
|
||||||
from werkzeug.security import check_password_hash, generate_password_hash
|
from werkzeug.security import check_password_hash, generate_password_hash
|
||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
@@ -15,6 +15,7 @@ from routes.test import test
|
|||||||
from routes.home import home
|
from routes.home import home
|
||||||
from routes.http import http
|
from routes.http import http
|
||||||
from routes.llm import llm
|
from routes.llm import llm
|
||||||
|
from routes.auth import auth
|
||||||
from flask_apscheduler import APScheduler
|
from flask_apscheduler import APScheduler
|
||||||
import asyncio
|
import asyncio
|
||||||
import aiohttp
|
import aiohttp
|
||||||
@@ -28,9 +29,8 @@ app = Flask(__name__)
|
|||||||
|
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
app.secret_key = os.environ.get('SECRET_KEY', '2a661781919643cb8a5a8bc57642d99f')
|
app.secret_key = os.environ.get('SECRET_KEY', '2a661781919643cb8a5a8bc57642d99f')
|
||||||
login_manager = LoginManager()
|
|
||||||
login_manager.init_app(app)
|
login_manager.init_app(app)
|
||||||
login_manager.login_view = "login"
|
login_manager.login_view = "auth.login"
|
||||||
jinja_partials.register_extensions(app)
|
jinja_partials.register_extensions(app)
|
||||||
|
|
||||||
# Remove scheduler configuration and initialization
|
# Remove scheduler configuration and initialization
|
||||||
@@ -41,21 +41,7 @@ app.register_blueprint(test, url_prefix='/test')
|
|||||||
app.register_blueprint(home, url_prefix='/home')
|
app.register_blueprint(home, url_prefix='/home')
|
||||||
app.register_blueprint(http, url_prefix='/http')
|
app.register_blueprint(http, url_prefix='/http')
|
||||||
app.register_blueprint(llm, url_prefix='/llm')
|
app.register_blueprint(llm, url_prefix='/llm')
|
||||||
|
app.register_blueprint(auth, url_prefix='/auth')
|
||||||
class User(UserMixin):
|
|
||||||
def __init__(self, id, username, password_hash, created_at):
|
|
||||||
self.id = id
|
|
||||||
self.username = username
|
|
||||||
self.password_hash = password_hash
|
|
||||||
self.created_at = created_at
|
|
||||||
|
|
||||||
@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'])
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Swith to inter app routing, which results in speed up from ~400ms to ~270ms
|
# Swith to inter app routing, which results in speed up from ~400ms to ~270ms
|
||||||
# https://stackoverflow.com/questions/76886643/linking-two-not-exposed-dokku-apps
|
# https://stackoverflow.com/questions/76886643/linking-two-not-exposed-dokku-apps
|
||||||
@@ -186,7 +172,7 @@ def execute_http_function(user_id, function):
|
|||||||
# Check if the function is public, if not check if the user is authenticated and owns the function
|
# Check if the function is public, if not check if the user is authenticated and owns the function
|
||||||
if not is_public:
|
if not is_public:
|
||||||
if not current_user.is_authenticated:
|
if not current_user.is_authenticated:
|
||||||
return login_manager.unauthorized()
|
return redirect(url_for('auth.login', next=request.url))
|
||||||
|
|
||||||
if int(current_user.id) != user_id:
|
if int(current_user.id) != user_id:
|
||||||
return jsonify({'error': 'Function belongs to another user'}), 404
|
return jsonify({'error': 'Function belongs to another user'}), 404
|
||||||
@@ -244,71 +230,6 @@ def execute_http_function(user_id, function):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({'error': str(e)}), 500
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
@app.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:
|
|
||||||
return render_template("login.html", error="User does not exist")
|
|
||||||
|
|
||||||
if not check_password_hash(user_data['password_hash'], 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'])
|
|
||||||
|
|
||||||
# user should be an instance of your `User` class
|
|
||||||
login_user(user)
|
|
||||||
#flask.flash('Logged in successfully.')
|
|
||||||
|
|
||||||
next = request.args.get('next')
|
|
||||||
return redirect(next or url_for('home.index'))
|
|
||||||
|
|
||||||
@app.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'])
|
|
||||||
login_user(user)
|
|
||||||
|
|
||||||
return redirect(url_for('home.index'))
|
|
||||||
|
|
||||||
@app.route("/logout")
|
|
||||||
@login_required
|
|
||||||
def logout():
|
|
||||||
logout_user()
|
|
||||||
return redirect(url_for('landing_page'))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@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'])
|
|
||||||
return None
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Bind to PORT if defined, otherwise default to 5000.
|
# Bind to PORT if defined, otherwise default to 5000.
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|||||||
from db import DataBase
|
from db import DataBase
|
||||||
from flask_htmx import HTMX
|
from flask_htmx import HTMX
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
from flask_login import LoginManager
|
||||||
|
|
||||||
db = DataBase()
|
db = DataBase()
|
||||||
htmx = HTMX()
|
htmx = HTMX()
|
||||||
|
login_manager = LoginManager()
|
||||||
|
|
||||||
environment = Environment(
|
environment = Environment(
|
||||||
loader=FileSystemLoader("templates"),
|
loader=FileSystemLoader("templates"),
|
||||||
|
|||||||
83
routes/auth.py
Normal file
83
routes/auth.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
from flask import Blueprint, render_template, request, redirect, url_for
|
||||||
|
from flask_login import login_user, logout_user, login_required, UserMixin
|
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
from extensions import db, login_manager
|
||||||
|
|
||||||
|
auth = Blueprint('auth', __name__)
|
||||||
|
|
||||||
|
class User(UserMixin):
|
||||||
|
def __init__(self, id, username, password_hash, created_at):
|
||||||
|
self.id = id
|
||||||
|
self.username = username
|
||||||
|
self.password_hash = password_hash
|
||||||
|
self.created_at = created_at
|
||||||
|
|
||||||
|
@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'])
|
||||||
|
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'])
|
||||||
|
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:
|
||||||
|
return render_template("login.html", error="User does not exist")
|
||||||
|
|
||||||
|
if not check_password_hash(user_data['password_hash'], 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'])
|
||||||
|
|
||||||
|
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'])
|
||||||
|
login_user(user)
|
||||||
|
|
||||||
|
return redirect(url_for('home.index'))
|
||||||
|
|
||||||
|
@auth.route("/logout")
|
||||||
|
@login_required
|
||||||
|
def logout():
|
||||||
|
logout_user()
|
||||||
|
return redirect(url_for('landing_page'))
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<a class="inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground rounded-full border border-gray-200 w-8 h-8 dark:border-gray-800 cursor-pointer"
|
<a class="inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground rounded-full border border-gray-200 w-8 h-8 dark:border-gray-800 cursor-pointer"
|
||||||
data-id="40" type="button" id="radix-:r1u:" aria-haspopup="menu" aria-expanded="false"
|
data-id="40" type="button" id="radix-:r1u:" aria-haspopup="menu" aria-expanded="false"
|
||||||
data-state="closed" href="{{ url_for('logout') }}">
|
data-state="closed" href="{{ url_for('auth.logout') }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
stroke="currentColor" class="w-6 h-6">
|
stroke="currentColor" class="w-6 h-6">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
<hr class="my-4" data-id="15">
|
<hr class="my-4" data-id="15">
|
||||||
<div class="text-center" data-id="16">
|
<div class="text-center" data-id="16">
|
||||||
<p class="text-gray-500" data-id="17">Don't have an account? <a class="underline text-blue-500"
|
<p class="text-gray-500" data-id="17">Don't have an account? <a class="underline text-blue-500"
|
||||||
data-id="18" href="{{ url_for('signup') }}" rel="ugc">Sign up</a>
|
data-id="18" href="{{ url_for('auth.signup') }}" rel="ugc">Sign up</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
<hr class="my-4" data-id="15">
|
<hr class="my-4" data-id="15">
|
||||||
<div class="text-center" data-id="16">
|
<div class="text-center" data-id="16">
|
||||||
<p class="text-gray-500" data-id="17">Already have an accont? <a class="underline text-blue-500"
|
<p class="text-gray-500" data-id="17">Already have an accont? <a class="underline text-blue-500"
|
||||||
data-id="18" href="{{ url_for('login') }}" rel="ugc">Login</a>
|
data-id="18" href="{{ url_for('auth.login') }}" rel="ugc">Login</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user