Add in authentication via Flask-Login, still need to modify http functions so they are linked to a user
This commit is contained in:
87
app.py
87
app.py
@@ -7,13 +7,35 @@ from flask_htmx import HTMX
|
|||||||
import requests
|
import requests
|
||||||
from db import DataBase
|
from db import DataBase
|
||||||
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, login_required, login_user
|
||||||
|
from werkzeug.security import check_password_hash, generate_password_hash
|
||||||
|
|
||||||
|
login_manager = LoginManager()
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
|
app.secret_key = os.environ.get('SECRET_KEY', '2a661781919643cb8a5a8bc57642d99f')
|
||||||
|
login_manager.init_app(app)
|
||||||
|
login_manager.login_view = "login"
|
||||||
jinja_partials.register_extensions(app)
|
jinja_partials.register_extensions(app)
|
||||||
htmx = HTMX(app)
|
htmx = HTMX(app)
|
||||||
db = DataBase(app)
|
db = DataBase(app)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
API_URL = 'https://isolator.peterstockings.com/execute'
|
API_URL = 'https://isolator.peterstockings.com/execute'
|
||||||
|
|
||||||
DEFAULT_FUNCTION_NAME = 'foo'
|
DEFAULT_FUNCTION_NAME = 'foo'
|
||||||
@@ -65,22 +87,26 @@ def client(function):
|
|||||||
return render_template("client.html", **http_function)
|
return render_template("client.html", **http_function)
|
||||||
|
|
||||||
@ app.route("/dashboard", methods=["GET"])
|
@ app.route("/dashboard", methods=["GET"])
|
||||||
|
@login_required
|
||||||
def dashboard():
|
def dashboard():
|
||||||
http_functions = db.get_http_functions()
|
http_functions = db.get_http_functions()
|
||||||
http_functions = create_http_functions_view_model(http_functions)
|
http_functions = create_http_functions_view_model(http_functions)
|
||||||
return render_template("dashboard.html", http_functions=http_functions)
|
return render_template("dashboard.html", http_functions=http_functions)
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions", methods=["GET"])
|
@ app.route("/dashboard/http_functions", methods=["GET"])
|
||||||
|
@login_required
|
||||||
def dashboard_http_functions():
|
def dashboard_http_functions():
|
||||||
http_functions = db.get_http_functions()
|
http_functions = db.get_http_functions()
|
||||||
http_functions = create_http_functions_view_model(http_functions)
|
http_functions = create_http_functions_view_model(http_functions)
|
||||||
return render_template("dashboard/http_functions.html", http_functions=http_functions)
|
return render_template("dashboard/http_functions.html", http_functions=http_functions)
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/add_form", methods=["GET"])
|
@ app.route("/dashboard/http_functions/add_form", methods=["GET"])
|
||||||
|
@login_required
|
||||||
def get_http_function_add_form():
|
def get_http_function_add_form():
|
||||||
return render_template("dashboard/http_functions/new.html", name=DEFAULT_FUNCTION_NAME, script=DEFAULT_SCRIPT, environment_info=DEFAULT_ENVIRONMENT)
|
return render_template("dashboard/http_functions/new.html", name=DEFAULT_FUNCTION_NAME, script=DEFAULT_SCRIPT, environment_info=DEFAULT_ENVIRONMENT)
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/create", methods=["POST"])
|
@ app.route("/dashboard/http_functions/create", methods=["POST"])
|
||||||
|
@login_required
|
||||||
def create_http_function():
|
def create_http_function():
|
||||||
try:
|
try:
|
||||||
name = request.json.get('name')
|
name = request.json.get('name')
|
||||||
@@ -97,6 +123,7 @@ def create_http_function():
|
|||||||
return { "status": "error", "message": str(e) }
|
return { "status": "error", "message": str(e) }
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/edit_form", methods=["GET"])
|
@ app.route("/dashboard/http_functions/edit_form", methods=["GET"])
|
||||||
|
@login_required
|
||||||
def get_http_function_edit_form():
|
def get_http_function_edit_form():
|
||||||
name = request.args.get('name')
|
name = request.args.get('name')
|
||||||
http_function = db.get_http_function(name)
|
http_function = db.get_http_function(name)
|
||||||
@@ -107,6 +134,7 @@ def get_http_function_edit_form():
|
|||||||
return render_template("dashboard/http_functions/edit.html", name=name, script=script, environment_info=environment_info)
|
return render_template("dashboard/http_functions/edit.html", name=name, script=script, environment_info=environment_info)
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/edit", methods=["POST"])
|
@ app.route("/dashboard/http_functions/edit", methods=["POST"])
|
||||||
|
@login_required
|
||||||
def edit_http_function():
|
def edit_http_function():
|
||||||
try:
|
try:
|
||||||
name = request.json.get('name')
|
name = request.json.get('name')
|
||||||
@@ -120,6 +148,7 @@ def edit_http_function():
|
|||||||
return { "status": "error", "message": str(e) }
|
return { "status": "error", "message": str(e) }
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/delete", methods=["DELETE"])
|
@ app.route("/dashboard/http_functions/delete", methods=["DELETE"])
|
||||||
|
@login_required
|
||||||
def delete_http_function():
|
def delete_http_function():
|
||||||
try:
|
try:
|
||||||
name = request.args.get('name')
|
name = request.args.get('name')
|
||||||
@@ -132,6 +161,7 @@ def delete_http_function():
|
|||||||
return jsonify({"status": 'error', "message": str(e)}), 500
|
return jsonify({"status": 'error', "message": str(e)}), 500
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/logs", methods=["GET"])
|
@ app.route("/dashboard/http_functions/logs", methods=["GET"])
|
||||||
|
@login_required
|
||||||
def get_http_function_logs():
|
def get_http_function_logs():
|
||||||
name = request.args.get('name')
|
name = request.args.get('name')
|
||||||
http_function = db.get_http_function(name)
|
http_function = db.get_http_function(name)
|
||||||
@@ -144,6 +174,7 @@ def get_http_function_logs():
|
|||||||
|
|
||||||
|
|
||||||
@ app.route("/dashboard/timer_functions", methods=["GET"])
|
@ app.route("/dashboard/timer_functions", methods=["GET"])
|
||||||
|
@login_required
|
||||||
def dashboard_timer_functions():
|
def dashboard_timer_functions():
|
||||||
return render_template("dashboard/timer_functions.html")
|
return render_template("dashboard/timer_functions.html")
|
||||||
|
|
||||||
@@ -227,6 +258,62 @@ def execute_http_function(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('dashboard'))
|
||||||
|
|
||||||
|
@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")
|
||||||
|
|
||||||
|
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('dashboard'))
|
||||||
|
|
||||||
|
|
||||||
|
@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.
|
||||||
|
|||||||
17
db.py
17
db.py
@@ -83,4 +83,19 @@ class DataBase():
|
|||||||
FROM http_function_invocations
|
FROM http_function_invocations
|
||||||
WHERE http_function_id=%s
|
WHERE http_function_id=%s
|
||||||
ORDER BY invocation_time DESC""", [http_function_id])
|
ORDER BY invocation_time DESC""", [http_function_id])
|
||||||
return http_function_invocations
|
return http_function_invocations
|
||||||
|
|
||||||
|
def get_user(self, user_id):
|
||||||
|
user = self.execute(
|
||||||
|
'SELECT id, username, password_hash, created_at FROM users WHERE id=%s', [int(user_id)], one=True)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def get_user_by_username(self, username):
|
||||||
|
user = self.execute(
|
||||||
|
'SELECT id, username, password_hash, created_at FROM users WHERE username=%s', [username], one=True)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def create_new_user(self, username, password_hash):
|
||||||
|
new_user = self.execute(
|
||||||
|
'INSERT INTO users (username, password_hash) VALUES (%s, %s) RETURNING id, username, password_hash, created_at', [username, password_hash], commit=True)
|
||||||
|
return new_user
|
||||||
@@ -7,4 +7,5 @@ flask-htmx==0.2.0
|
|||||||
python-dateutil==2.8.2
|
python-dateutil==2.8.2
|
||||||
jinja2-fragments==0.3.0
|
jinja2-fragments==0.3.0
|
||||||
Werkzeug==2.2.2
|
Werkzeug==2.2.2
|
||||||
requests==2.26.0
|
requests==2.26.0
|
||||||
|
Flask-Login==0.6.3
|
||||||
57
templates/login.html
Normal file
57
templates/login.html
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="w-full h-screen flex items-center justify-center bg-gray-100" data-id="1">
|
||||||
|
<div class="rounded-lg border bg-card text-card-foreground shadow-sm w-full max-w-md mx-4" data-id="2"
|
||||||
|
data-v0-t="card">
|
||||||
|
<div class="flex flex-col space-y-1.5 p-6" data-id="3">
|
||||||
|
<h1 class="text-3xl font-bold text-center" data-id="4">
|
||||||
|
Login
|
||||||
|
</h1>
|
||||||
|
{% if error %}
|
||||||
|
<h2 class="text-2xl font-bold text-center text-red-500" data-id="4">
|
||||||
|
{{ error }}
|
||||||
|
</h2>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<form method="POST" class="p-4 space-y-4" data-id="5">
|
||||||
|
<div class="space-y-2" data-id="6">
|
||||||
|
<label
|
||||||
|
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 {% if error %}text-red-500 {% endif %}"
|
||||||
|
for="username" data-id="7">
|
||||||
|
Username
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class="flex h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 w-full {% if error %}border-red-500 {% endif %}"
|
||||||
|
name="username" placeholder="Enter your username" required="" data-id="8" type="text">
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2" data-id="9">
|
||||||
|
<label
|
||||||
|
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 {% if error %}text-red-500 {% endif %}"
|
||||||
|
for="password" data-id="10">Password</label>
|
||||||
|
<input
|
||||||
|
class="flex h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 w-full {% if error %}border-red-500 {% endif %}"
|
||||||
|
name="password" placeholder="Enter your password" required="" data-id="11" type="password">
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center" data-id="12">
|
||||||
|
<a class="text-sm underline text-gray-500" data-id="13" href="#" rel="ugc">
|
||||||
|
Forgot password?
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
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-primary/90 h-10 px-6 py-2 bg-blue-500 text-white rounded-md"
|
||||||
|
type="submit" data-id="14">
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<hr class="my-4" data-id="15">
|
||||||
|
<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"
|
||||||
|
data-id="18" href="{{ url_for('signup') }}" rel="ugc">Sign up</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
48
templates/signup.html
Normal file
48
templates/signup.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="w-full h-screen flex items-center justify-center bg-gray-100" data-id="1">
|
||||||
|
<div class="rounded-lg border bg-card text-card-foreground shadow-sm w-full max-w-md mx-4" data-id="2"
|
||||||
|
data-v0-t="card">
|
||||||
|
<div class="flex flex-col space-y-1.5 p-6" data-id="3">
|
||||||
|
<h1 class="text-3xl font-bold text-center" data-id="4">
|
||||||
|
Signup
|
||||||
|
</h1>
|
||||||
|
{% if error %}
|
||||||
|
<h2 class="text-2xl font-bold text-center text-red-500" data-id="4">
|
||||||
|
{{ error }}
|
||||||
|
</h2>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<form method="POST" class="p-4 space-y-4" data-id="5">
|
||||||
|
<div class="space-y-2" data-id="6">
|
||||||
|
<label
|
||||||
|
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 {% if error %}text-red-500 {% endif %}"
|
||||||
|
for="username" data-id="7">
|
||||||
|
Username
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class="flex h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 w-full {% if error %}border-red-500 {% endif %}"
|
||||||
|
name="username" placeholder="Enter your username" required="" data-id="8" type="text">
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2" data-id="9">
|
||||||
|
<label
|
||||||
|
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 {% if error %}text-red-500 {% endif %}"
|
||||||
|
for="password" data-id="10">Password</label>
|
||||||
|
<input
|
||||||
|
class="flex h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 w-full {% if error %}border-red-500 {% endif %}"
|
||||||
|
name="password" placeholder="Enter your password" required="" data-id="11" type="password">
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center" data-id="12">
|
||||||
|
<button
|
||||||
|
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-primary/90 h-10 px-6 py-2 bg-blue-500 text-white rounded-md"
|
||||||
|
type="submit" data-id="14">
|
||||||
|
Signup
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user