From b150801267a17c402d8b1a84960df250f6b38a0c Mon Sep 17 00:00:00 2001 From: Paul Brinkmeier Date: Tue, 22 Aug 2023 03:11:52 +0200 Subject: [PATCH] Use flask-login instead of client-side sessions for storing selected location and orders --- jon/__init__.py | 7 +-- jon/auth.py | 79 ++++++++++++++++++++++++------- jon/entry.py | 28 ++++------- jon/inventory.py | 7 +-- jon/location.py | 12 ++--- jon/static/jon.css | 3 ++ jon/templates/base.html | 14 +++++- jon/templates/entry/index.html | 2 +- jon/templates/location/index.html | 4 +- 9 files changed, 99 insertions(+), 57 deletions(-) diff --git a/jon/__init__.py b/jon/__init__.py index 6195cdf..c375ac4 100644 --- a/jon/__init__.py +++ b/jon/__init__.py @@ -22,12 +22,7 @@ def create_app(): app.config.from_file("config.json", load=json.load, silent=True) db.init_app(app) - - # This function denies every request until `auth.ACCESS_TOKEN` - # is passed using `?token=` to authenticate the session. - @app.before_request - def before_req_fun(): - return auth.before_request() + auth.init_app(app) @app.context_processor def utility_processor(): diff --git a/jon/auth.py b/jon/auth.py index 10d7fbb..65c1969 100644 --- a/jon/auth.py +++ b/jon/auth.py @@ -1,7 +1,10 @@ import secrets import string -from flask import Blueprint, request, redirect, render_template, session +from flask import Blueprint, request, redirect, render_template +from flask_login import current_user, login_user, logout_user, LoginManager +from typing import Any, Dict, List, Optional + bp = Blueprint("auth", __name__, url_prefix="/auth") @@ -15,27 +18,67 @@ ALLOWED_PATHS = [ ] -def before_request(): - """ - If the correct token query parameter is passed along with any request, - we mark this session authenticated by setting `session["authenticated"]`. - Unless the session is authenticated, all requests result in a 403 FORBIDDEN. - """ - if "token" in request.args: - if request.args["token"] == ACCESS_TOKEN: - session["authenticated"] = () - # Reload the page without query parameters - return redirect(request.path) +# A poor man's replacement for memory-backed session solution. +# We keep exactly one User (and the corresponding UserData) in +# memory and use that to store session data. +class UserData: + location: Optional[Dict[str, Any]] + orders: List[Dict[str, Any]] - # Don't deny any paths in `ALLOWED_PATHS` - if request.path in ALLOWED_PATHS: - return + def __init__(self): + self.location = None + self.orders = [] - if not "authenticated" in session: - return render_template("auth/denied.html"), 403 + +class User: + is_authenticated: bool + is_active: bool + is_anonymous: bool + + data: UserData + + def __init__(self): + self.is_authenticated = True + self.is_active = True + self.is_anonymous = False + + self.data = UserData() + + def get_id(self) -> str: + return "" + + +def init_app(app): + login_manager = LoginManager(app) + the_one_and_only_user = User() + + @login_manager.user_loader + def load_user(user_id: str) -> User: + assert user_id == "" + return the_one_and_only_user + + # This function denies every request until `auth.ACCESS_TOKEN` + # is passed using `?token=` to authenticate the user. + # We use this instead of @login_required because otherwise we'd have + # to add that annotation to all routes. + # See also: https://flask-login.readthedocs.io/en/latest/#flask_login.login_required + @app.before_request + def before_request(): + if "token" in request.args: + if request.args["token"] == ACCESS_TOKEN: + login_user(the_one_and_only_user) + # Reload the page without query parameters + return redirect(request.path) + + # Never deny any paths in `ALLOWED_PATHS` + if request.path in ALLOWED_PATHS: + return + + if not current_user.is_authenticated: + return render_template("auth/denied.html"), 403 @bp.get("/logout") def logout(): - session.pop("authenticated", None) + logout_user() return redirect("/") diff --git a/jon/entry.py b/jon/entry.py index 7b6c780..733f8f0 100644 --- a/jon/entry.py +++ b/jon/entry.py @@ -1,5 +1,5 @@ -from flask import Blueprint, flash, redirect, render_template, request, session - +from flask import Blueprint, flash, redirect, render_template, request +from flask_login import current_user from . import db @@ -9,22 +9,18 @@ bp = Blueprint("entry", __name__, url_prefix="/entry") @bp.route("/", methods=["GET", "POST"]) def index(): - cart = session.get("cart", default=[]) return render_template( - "entry/index.html", - cart=cart + "entry/index.html" ) @bp.post("/add-new-items") def add_new_entries(): - print(session) - i_know_what_im_doing = "i-know-what-im-doing" in request.form if not i_know_what_im_doing: return "Du weißt nicht was du tust", 400 - orders = session.get("cart", default=[]) + orders = current_user.data.orders if not orders: return "Keine Aufträge", 404 @@ -36,7 +32,7 @@ def add_new_entries(): db.get_db().commit() # Reset the cart - session["cart"] = [] + current_user.data.orders = [] return redirect(request.referrer) @@ -48,9 +44,7 @@ def delete_order(): except: return "Incomplete or mistyped form", 400 - cart = session.get("cart", default=[]) - del cart[order_index] - session["cart"] = cart + del current_user.data.orders[order_index] return redirect(request.referrer) @@ -76,9 +70,7 @@ def new_order(): except: return f"Incomplete or mistyped form", 400 - cart = session.get("cart", default=[]) - print(cart) - cart.append({ + current_user.data.orders.append({ "barcode": barcode, "name": name, "sales_units": sales_units, @@ -91,7 +83,6 @@ def new_order(): "net_unit_price": net_unit_price, "gross_unit_price": gross_unit_price }) - session["cart"] = cart return redirect("/entry") with db.run_query("entry/get_groups.sql") as cursor: @@ -122,10 +113,9 @@ def api_search_items(): except: return {"error": "Missing query parameter `search-term`"}, 400 - location = session.get("location", None) - + location = current_user.data.location with db.run_query("search_items.sql", { - "location_id": None if location is None else location["location_id"], + "location_id": location["location_id"] if location else None, "search_term": search_term }) as cursor: items = cursor.fetchall() diff --git a/jon/inventory.py b/jon/inventory.py index 43aafad..7fe9254 100644 --- a/jon/inventory.py +++ b/jon/inventory.py @@ -1,4 +1,5 @@ -from flask import Blueprint, redirect, render_template, request, session +from flask import Blueprint, redirect, render_template, request +from flask_login import current_user from . import db @@ -8,7 +9,7 @@ bp = Blueprint("inventory", __name__, url_prefix="/inventory") @bp.get("/") def index(): - location = session.get("location", None) + location = current_user.data.location items = db.run_query("get_inventory_overview.sql", { "location_id": None if location is None else location["location_id"] }).fetchall() @@ -20,7 +21,7 @@ def index(): @bp.get("/report") def read_report(): - location = session.get("location", None) + location = current_user.data.location items = db.run_query("get_inventory_report.sql", { "location_id": None if location is None else location["location_id"] }).fetchall() diff --git a/jon/location.py b/jon/location.py index daf84bd..bcd324c 100644 --- a/jon/location.py +++ b/jon/location.py @@ -1,4 +1,5 @@ -from flask import Blueprint, render_template, request, session +from flask import Blueprint, render_template, request +from flask_login import current_user from . import db @@ -11,13 +12,12 @@ def index(): if request.method == "POST": location_id = request.form.get("location_id", "") if location_id == "": - session.pop("location", None) + current_user.data.location = None else: location = db.run_query("get_location_by_id.sql", { - "location_id": location_id} - ).fetchone() - session["location"] = location - + "location_id": location_id + }).fetchone() + current_user.data.location = location locations = db.run_query("get_locations.sql").fetchall() diff --git a/jon/static/jon.css b/jon/static/jon.css index 0085d72..9f92248 100644 --- a/jon/static/jon.css +++ b/jon/static/jon.css @@ -71,3 +71,6 @@ th { display: block; width: 8em; } +details { + font-size: 0.8em; +} diff --git a/jon/templates/base.html b/jon/templates/base.html index c4af7be..54a0654 100644 --- a/jon/templates/base.html +++ b/jon/templates/base.html @@ -15,10 +15,10 @@ Eintragen - {% if "location" not in session %} + {% if not current_user.data.location %} Raum wählen {% else %} - Raum: {{ session.location.location_name }} + Raum: {{ current_user.data.location.location_name }} {% endif %} @@ -30,6 +30,16 @@
config
{% for key, value in config.items() %}{{ key }} = {{ value }}
+{% endfor %}
+
+
+ session +
{% for key, value in session.items() %}{{ key }} = {{ value }}
+{% endfor %}
+
+
+ current_user.data +
{% for key, value in current_user.data.__dict__.items() %}{{ key }} = {{ value }}
 {% endfor %}
{% endif %} diff --git a/jon/templates/entry/index.html b/jon/templates/entry/index.html index e08c717..126464f 100644 --- a/jon/templates/entry/index.html +++ b/jon/templates/entry/index.html @@ -15,7 +15,7 @@ VK-Preis (Brutto) Aktionen - {% for cart_item in cart %} + {% for cart_item in current_user.data.orders %} {{ cart_item.barcode }} {{ cart_item.name }} diff --git a/jon/templates/location/index.html b/jon/templates/location/index.html index c395213..286fd6a 100644 --- a/jon/templates/location/index.html +++ b/jon/templates/location/index.html @@ -3,9 +3,9 @@ {% block content %}