Compare commits

...

3 Commits

10 changed files with 100 additions and 57 deletions

View File

@ -22,12 +22,7 @@ def create_app():
app.config.from_file("config.json", load=json.load, silent=True) app.config.from_file("config.json", load=json.load, silent=True)
db.init_app(app) db.init_app(app)
auth.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()
@app.context_processor @app.context_processor
def utility_processor(): def utility_processor():

View File

@ -1,7 +1,10 @@
import secrets import secrets
import string 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") bp = Blueprint("auth", __name__, url_prefix="/auth")
@ -15,27 +18,67 @@ ALLOWED_PATHS = [
] ]
def before_request(): # A poor man's replacement for memory-backed session solution.
""" # We keep exactly one User (and the corresponding UserData) in
If the correct token query parameter is passed along with any request, # memory and use that to store session data.
we mark this session authenticated by setting `session["authenticated"]`. class UserData:
Unless the session is authenticated, all requests result in a 403 FORBIDDEN. location: Optional[Dict[str, Any]]
""" orders: List[Dict[str, Any]]
if "token" in request.args:
if request.args["token"] == ACCESS_TOKEN:
session["authenticated"] = ()
# Reload the page without query parameters
return redirect(request.path)
# Don't deny any paths in `ALLOWED_PATHS` def __init__(self):
if request.path in ALLOWED_PATHS: self.location = None
return 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") @bp.get("/logout")
def logout(): def logout():
session.pop("authenticated", None) logout_user()
return redirect("/") return redirect("/")

View File

@ -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 from . import db
@ -9,22 +9,18 @@ bp = Blueprint("entry", __name__, url_prefix="/entry")
@bp.route("/", methods=["GET", "POST"]) @bp.route("/", methods=["GET", "POST"])
def index(): def index():
cart = session.get("cart", default=[])
return render_template( return render_template(
"entry/index.html", "entry/index.html"
cart=cart
) )
@bp.post("/add-new-items") @bp.post("/add-new-items")
def add_new_entries(): def add_new_entries():
print(session)
i_know_what_im_doing = "i-know-what-im-doing" in request.form i_know_what_im_doing = "i-know-what-im-doing" in request.form
if not i_know_what_im_doing: if not i_know_what_im_doing:
return "Du weißt nicht was du tust", 400 return "Du weißt nicht was du tust", 400
orders = session.get("cart", default=[]) orders = current_user.data.orders
if not orders: if not orders:
return "Keine Aufträge", 404 return "Keine Aufträge", 404
@ -36,7 +32,7 @@ def add_new_entries():
db.get_db().commit() db.get_db().commit()
# Reset the cart # Reset the cart
session["cart"] = [] current_user.data.orders = []
return redirect(request.referrer) return redirect(request.referrer)
@ -48,9 +44,7 @@ def delete_order():
except: except:
return "Incomplete or mistyped form", 400 return "Incomplete or mistyped form", 400
cart = session.get("cart", default=[]) del current_user.data.orders[order_index]
del cart[order_index]
session["cart"] = cart
return redirect(request.referrer) return redirect(request.referrer)
@ -76,9 +70,7 @@ def new_order():
except: except:
return f"Incomplete or mistyped form", 400 return f"Incomplete or mistyped form", 400
cart = session.get("cart", default=[]) current_user.data.orders.append({
print(cart)
cart.append({
"barcode": barcode, "barcode": barcode,
"name": name, "name": name,
"sales_units": sales_units, "sales_units": sales_units,
@ -91,7 +83,6 @@ def new_order():
"net_unit_price": net_unit_price, "net_unit_price": net_unit_price,
"gross_unit_price": gross_unit_price "gross_unit_price": gross_unit_price
}) })
session["cart"] = cart
return redirect("/entry") return redirect("/entry")
with db.run_query("entry/get_groups.sql") as cursor: with db.run_query("entry/get_groups.sql") as cursor:
@ -122,10 +113,9 @@ def api_search_items():
except: except:
return {"error": "Missing query parameter `search-term`"}, 400 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", { 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 "search_term": search_term
}) as cursor: }) as cursor:
items = cursor.fetchall() items = cursor.fetchall()

View File

@ -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 from . import db
@ -8,7 +9,7 @@ bp = Blueprint("inventory", __name__, url_prefix="/inventory")
@bp.get("/") @bp.get("/")
def index(): def index():
location = session.get("location", None) location = current_user.data.location
items = db.run_query("get_inventory_overview.sql", { items = db.run_query("get_inventory_overview.sql", {
"location_id": None if location is None else location["location_id"] "location_id": None if location is None else location["location_id"]
}).fetchall() }).fetchall()
@ -20,7 +21,7 @@ def index():
@bp.get("/report") @bp.get("/report")
def read_report(): def read_report():
location = session.get("location", None) location = current_user.data.location
items = db.run_query("get_inventory_report.sql", { items = db.run_query("get_inventory_report.sql", {
"location_id": None if location is None else location["location_id"] "location_id": None if location is None else location["location_id"]
}).fetchall() }).fetchall()

View File

@ -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 from . import db
@ -11,13 +12,12 @@ def index():
if request.method == "POST": if request.method == "POST":
location_id = request.form.get("location_id", "") location_id = request.form.get("location_id", "")
if location_id == "": if location_id == "":
session.pop("location", None) current_user.data.location = None
else: else:
location = db.run_query("get_location_by_id.sql", { location = db.run_query("get_location_by_id.sql", {
"location_id": location_id} "location_id": location_id
).fetchone() }).fetchone()
session["location"] = location current_user.data.location = location
locations = db.run_query("get_locations.sql").fetchall() locations = db.run_query("get_locations.sql").fetchall()

View File

@ -71,3 +71,6 @@ th {
display: block; display: block;
width: 8em; width: 8em;
} }
details {
font-size: 0.8em;
}

View File

@ -15,10 +15,10 @@
<li{{ " class=current-page" if request.path.startswith("/entry") else "" }}><a href="/entry">Eintragen</a></li> <li{{ " class=current-page" if request.path.startswith("/entry") else "" }}><a href="/entry">Eintragen</a></li>
<li{{ " class=current-page" if request.path.startswith("/location") else "" }}> <li{{ " class=current-page" if request.path.startswith("/location") else "" }}>
<a href="/location"> <a href="/location">
{% if "location" not in session %} {% if not current_user.data.location %}
Raum wählen Raum wählen
{% else %} {% else %}
Raum: {{ session.location.location_name }} Raum: {{ current_user.data.location.location_name }}
{% endif %} {% endif %}
</a> </a>
</li> </li>
@ -30,6 +30,16 @@
<details> <details>
<summary><code>config</code></summary> <summary><code>config</code></summary>
<pre>{% for key, value in config.items() %}{{ key }} = {{ value }} <pre>{% for key, value in config.items() %}{{ key }} = {{ value }}
{% endfor %}</pre>
</details>
<details>
<summary><code>session</code></summary>
<pre>{% for key, value in session.items() %}{{ key }} = {{ value }}
{% endfor %}</pre>
</details>
<details>
<summary><code>current_user.data</code></summary>
<pre>{% for key, value in current_user.data.__dict__.items() %}{{ key }} = {{ value }}
{% endfor %}</pre> {% endfor %}</pre>
</details> </details>
{% endif %} {% endif %}

View File

@ -15,7 +15,7 @@
<th>VK-Preis (Brutto)</th> <th>VK-Preis (Brutto)</th>
<th>Aktionen</th> <th>Aktionen</th>
</tr> </tr>
{% for cart_item in cart %} {% for cart_item in current_user.data.orders %}
<tr> <tr>
<td><code>{{ cart_item.barcode }}</code></td> <td><code>{{ cart_item.barcode }}</code></td>
<td>{{ cart_item.name }}</td> <td>{{ cart_item.name }}</td>

View File

@ -3,9 +3,9 @@
{% block content %} {% block content %}
<form method="POST"> <form method="POST">
<select name="location_id"> <select name="location_id">
<option value="" {{ "selected" if "location" not in session else ""}}>-</option> <option value="" {{ "selected" if not current_user.data.location else ""}}>-</option>
{% for location in locations %} {% for location in locations %}
<option value="{{ location.location_id }}" {{ "selected" if "location" in session and session.location.location_id == location.location_id else "" }}>{{ location.location_name }}</option> <option value="{{ location.location_id }}" {{ "selected" if current_user.data.location.location_id == location.location_id else "" }}>{{ location.location_name }}</option>
{% endfor %} {% endfor %}
</select> </select>

View File

@ -1,6 +1,7 @@
blinker==1.6.2 blinker==1.6.2
click==8.1.3 click==8.1.3
Flask==2.3.2 Flask==2.3.2
Flask-Login==0.6.2
itsdangerous==2.1.2 itsdangerous==2.1.2
Jinja2==3.1.2 Jinja2==3.1.2
MarkupSafe==2.1.2 MarkupSafe==2.1.2