From 86ea7c0000cd7daf34ad21431119a21ae8300474 Mon Sep 17 00:00:00 2001 From: W13R <9070224-W13R@users.noreply.gitlab.com> Date: Sat, 15 Oct 2022 19:37:01 +0200 Subject: [PATCH 01/67] Added 'supply' page to create negative register transactions, updated translation --- application/app/admin.py | 4 + application/app/models.py | 1 + application/app/templates/supply.html | 61 +++++++ application/app/templates/userPanel.html | 3 + application/app/urls.py | 2 + application/app/views.py | 36 +++- application/locale/de/LC_MESSAGES/django.mo | Bin 3469 -> 3806 bytes application/locale/de/LC_MESSAGES/django.po | 177 ++++++++++++-------- static/css/supply.css | 6 + static/js/supply.js | 56 +++++++ 10 files changed, 278 insertions(+), 68 deletions(-) create mode 100644 application/app/templates/supply.html create mode 100644 static/css/supply.css create mode 100644 static/js/supply.js diff --git a/application/app/admin.py b/application/app/admin.py index 7e81374..e72e506 100644 --- a/application/app/admin.py +++ b/application/app/admin.py @@ -49,6 +49,10 @@ class CustomUserAdmin(UserAdmin): {"fields": ("balance", "allow_order_with_negative_balance")}, )) fieldsets_.insert(2, ( + "Supply", + {"fields": ("allowed_to_supply",)}, + )) + fieldsets_.insert(3, ( "Profile Picture", {"fields": ("profile_picture_filename",)}, )) diff --git a/application/app/models.py b/application/app/models.py index 370a0dd..b92672c 100644 --- a/application/app/models.py +++ b/application/app/models.py @@ -21,6 +21,7 @@ class User(AbstractUser): balance = models.DecimalField(max_digits=8, decimal_places=2, default=0.00) allow_order_with_negative_balance = models.BooleanField(default=False) profile_picture_filename = models.CharField(default="default.svg", max_length=25) + allowed_to_supply = models.BooleanField(default=False) def delete(self, *args, **kwargs): self.balance = 0 diff --git a/application/app/templates/supply.html b/application/app/templates/supply.html new file mode 100644 index 0000000..0f62ad5 --- /dev/null +++ b/application/app/templates/supply.html @@ -0,0 +1,61 @@ +{% extends "baseLayout.html" %} + +{% load i18n %} +{% load l10n %} + +{% block title %} +{% translate "Drinks - Supply" %} +{% endblock %} + +{% block headAdditional %} + + +{% endblock %} + +{% block heading %} + {% translate "Supply" %} +{% endblock %} + +{% block content %} + + {% if user.is_superuser or user.allowed_to_supply %} + +
+ {% csrf_token %} + +
+
{% translate "Description" %}:
+ +
+ +
+
{% translate "Price" %} ({{ currency_suffix }}):
+
+ +
+
+ +
+ +
+ {% translate "cancel" %} + +
+ +
+ + + + + {% else %} + +
+

{% translate "You are not allowed to view this site." %}

+ {% translate "back" %} +
+ + {% endif %} + + + +{% endblock %} diff --git a/application/app/templates/userPanel.html b/application/app/templates/userPanel.html index b634cd3..447c2d8 100644 --- a/application/app/templates/userPanel.html +++ b/application/app/templates/userPanel.html @@ -32,6 +32,9 @@ {% if user.is_superuser or user.is_staff %} Admin Panel {% endif %} + {% if user.is_superuser or user.allowed_to_supply %} + {% translate "Supply" %} + {% endif %} {% translate "Change Password" %} diff --git a/application/app/urls.py b/application/app/urls.py index c7a221a..e720876 100644 --- a/application/app/urls.py +++ b/application/app/urls.py @@ -10,6 +10,7 @@ urlpatterns = [ path('history/', views.history), path('deposit/', views.deposit), path('statistics/', views.statistics), + path('supply/', views.supply), path('accounts/login/', views.login_page, name="login"), path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'), path('accounts/password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'), @@ -18,5 +19,6 @@ urlpatterns = [ # API # path('api/order-drink', views.api_order_drink), path('api/deposit', views.api_deposit), + path('api/supply', views.api_supply) #path('api/get-statistics', views.api_get_statistics) ] \ No newline at end of file diff --git a/application/app/views.py b/application/app/views.py index 152d8bd..8511249 100644 --- a/application/app/views.py +++ b/application/app/views.py @@ -103,6 +103,10 @@ def statistics(request): } return render(request, "statistics.html", context) +@login_required +def supply(request): + return render(request, "supply.html") + @login_required def redirect_home(request): return HttpResponseRedirect("/") @@ -132,7 +136,7 @@ def api_order_drink(request): else: return HttpResponse("notAvailable", status=400) - else: raise Exception("Balance below zero.") + else: raise Exception("Unexpected input or missing privileges.") except Exception as e: print(f"An exception occured while processing an order: User: {user.username} - Exception: {e}", file=sys.stderr) @@ -163,5 +167,33 @@ def api_deposit(request): else: raise Exception("Deposit amount too big or small.") except Exception as e: - print(f"An exception occured while processing an transaction: User: {user.username} - Exception: {e}", file=sys.stderr) + print(f"An exception occured while processing a transaction: User: {user.username} - Exception: {e}", file=sys.stderr) + return HttpResponse(b"", status=500) + +@login_required +def api_supply(request): + + # check request -> supply + + user = request.user + + try: + + price = decimal.Decimal(request.POST["supplyPrice"]) + description = str(request.POST["supplyDescription"]) + + if 0.00 < price < 9999.99 and (user.allowed_to_supply or user.is_superuser): + # create transaction + RegisterTransaction.objects.create( + transaction_sum=-price, + comment=f"Supply: {description}", + is_user_deposit=False, + user=user + ) + # + return HttpResponse("success", status=200) + else: raise Exception("Unexpected input or missing privileges.") + + except Exception as e: + print(f"An exception occured while processing a supply transaction: User: {user.username} - Exception: {e}", file=sys.stderr) return HttpResponse(b"", status=500) diff --git a/application/locale/de/LC_MESSAGES/django.mo b/application/locale/de/LC_MESSAGES/django.mo index 07c684c8c574ed25adf7f9061611ea3ad5285e44..1f4683e4dba324f38b3068033447107f11c95ead 100644 GIT binary patch delta 1595 zcmYk+TWC~A9LMpA=GJ86B{p7@I;mzgO^h{F6RV{lYEfH=ET}IQH_0S9WOEYEp7j!> zt0)S=LSd<3iDtmC4P_fqPrNNLCWTXTxlq=Y8B>AH_#tk4c2kM7kU^VWs}Cy+n$2NxaDKd9%=P)9TE+h5@x z>Sd%)6Lz6CbO4pXbT#?c03i)Z^%-O><|3;7GU`nKL=F4^_0m1}&);~f7)=?o-cHnp zy08fkqQ?CWx8ONn|2skcKcV3s4NGwbS+tqQ8^x_zhT2&YYq1rX(|qaM_xk!iY^D7W zYN9dx3=6msCsCPvi|etT7sle(d2Te}5NgH8P$@cznrIA_x(lc?zJ!`!9F>_XzJ43E z&`DJPhp2u}krOt{2_2ov3ZjkBQRh3jyes;CHMq)}S&G*=T$d3_NWxvhe5e4C3)~>Uu+{j4gc;RsQPfeyP3VNI!agp5}yCIW`({YfEgPyEO z<*3g7vbG|*%BCY{!{OMbGnwI|uGhxH_DJB4+IS$y+H4TJw!+4WN9EaE_fQZ|%&pAN zNo;2Y1Ca~5b76nsRMq_@U%5EC^J_ThSVi$+y01^q_Et|78WYLt_uF8;raR6w&TS^m%0_KQ8*9@1D95NpAFC`3q=MQ+wX01K7)59UxoB0J zOH@!$)TTuQO|=LVqKJ@L8H7+sXhG1*ASjCJ|6GsI;lAg0&-LE(@t!lzG+)VNV-~%k+1g^wSScAWCGe%=(ORr2-A23>+vM= ze%VI2;j%|mdhsbX;5aVFx5#Cmsnp?D=Wq0B`;4v*v$zbqojcs~{iuFVU^AXVF1t+C z!T5I7J$Q~<;R~$Acc_79F@;}H1ANCMdi1I!rBRuyN0MMYSi}-)A;+*C&*Cb)h04$q z^cdeJxS>ybiCi{GrGckV15CU22UH4YUHhB!hw~3Mv3*`*p`SdXwD5iryKn&4;yG0R z*Ri~vo7>!IMboGm&$$1~LbG=$=1AZBZ4~ z-$&HhnNN~`eV8EMvIUi*K4dXAhy^@^>hJ4>~ zPgX*$d^dLDo)r04$HP46!%*bkt4Jv zI{7-hi$#O$=UVQh($7pqd!lq|=K6o2)RBPj@IZK&l;&PFC|a4aq0`+)EEd)juCf(- z+(TtAODJ3VwWyFWeY%k960RY%|BZyoIzs8RN+Fhy1P5yF1+ng>_ diff --git a/application/locale/de/LC_MESSAGES/django.po b/application/locale/de/LC_MESSAGES/django.po index 22c05b2..997e989 100644 --- a/application/locale/de/LC_MESSAGES/django.po +++ b/application/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-06-13 19:01+0200\n" +"POT-Creation-Date: 2022-10-15 19:20+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Julian Müller (W13R)\n" "Language: DE\n" @@ -17,221 +17,266 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: app/templates/admin/base_site.html:7 +#: application/app/templates/admin/base_site.html:7 msgid "Django site admin" msgstr "Django Administrator" -#: app/templates/admin/base_site.html:15 +#: application/app/templates/admin/base_site.html:15 msgid "Django administration" msgstr "Django Administration" -#: app/templates/baseLayout.html:41 +#: application/app/templates/baseLayout.html:41 msgid "An error occured. Please log out and log in again." msgstr "Ein Fehler ist aufgetreten. Bitte ab- und wieder anmelden." -#: app/templates/deposit.html:6 +#: application/app/templates/deposit.html:6 msgid "Drinks - Deposit" msgstr "Getränke - Einzahlen" -#: app/templates/deposit.html:14 app/templates/userPanel.html:19 +#: application/app/templates/deposit.html:14 +#: application/app/templates/userPanel.html:23 msgid "Deposit" msgstr "Einzahlen" -#: app/templates/deposit.html:23 +#: application/app/templates/deposit.html:23 msgid "Amount" msgstr "Summe" -#: app/templates/deposit.html:31 app/templates/order.html:72 -#: app/templates/registration/login.html:56 +#: application/app/templates/deposit.html:31 +#: application/app/templates/order.html:72 +#: application/app/templates/registration/login.html:57 +#: application/app/templates/supply.html:41 msgid "cancel" msgstr "Abbrechen" -#: app/templates/deposit.html:32 +#: application/app/templates/deposit.html:32 msgid "confirm" msgstr "Bestätigen" -#: app/templates/history.html:6 +#: application/app/templates/history.html:6 msgid "Drinks - History" msgstr "Getränke - Verlauf" -#: app/templates/history.html:14 app/templates/userPanel.html:25 +#: application/app/templates/history.html:14 +#: application/app/templates/userPanel.html:30 msgid "History" msgstr "Verlauf" -#: app/templates/history.html:22 +#: application/app/templates/history.html:22 msgid "last 30 actions" msgstr "letzte 30 Vorgänge" -#: app/templates/history.html:33 app/templates/statistics.html:41 -#: app/templates/statistics.html:61 app/templates/statistics.html:81 -#: app/templates/statistics.html:101 app/templates/statistics.html:121 -#: app/templates/statistics.html:141 +#: application/app/templates/history.html:33 +#: application/app/templates/statistics.html:41 +#: application/app/templates/statistics.html:61 +#: application/app/templates/statistics.html:81 +#: application/app/templates/statistics.html:101 +#: application/app/templates/statistics.html:121 +#: application/app/templates/statistics.html:141 msgid "No history." msgstr "Kein Verlauf verfügbar." -#: app/templates/index.html:6 +#: application/app/templates/index.html:6 msgid "Drinks - Home" msgstr "Getränke - Home" -#: app/templates/index.html:14 +#: application/app/templates/index.html:14 msgid "Available Drinks" msgstr "Verfügbare Getränke" -#: app/templates/index.html:27 app/templates/index.html:34 +#: application/app/templates/index.html:27 +#: application/app/templates/index.html:34 msgid "available" msgstr "verfügbar" -#: app/templates/index.html:43 +#: application/app/templates/index.html:43 msgid "No drinks available." msgstr "Es sind gerade keine Getränke verfügbar." -#: app/templates/order.html:7 +#: application/app/templates/order.html:7 msgid "Drinks - Order" msgstr "Getränke - Bestellen" -#: app/templates/order.html:16 +#: application/app/templates/order.html:16 +#: packages/django/forms/formsets.py:405 packages/django/forms/formsets.py:412 msgid "Order" msgstr "Bestellung" -#: app/templates/order.html:29 +#: application/app/templates/order.html:29 msgid "Drink" msgstr "Getränk" -#: app/templates/order.html:34 +#: application/app/templates/order.html:34 msgid "Price per Item" msgstr "Preis pro Getränk" -#: app/templates/order.html:40 +#: application/app/templates/order.html:40 msgid "Available" msgstr "Verfügbar" -#: app/templates/order.html:46 +#: application/app/templates/order.html:46 msgid "Count" msgstr "Anzahl" -#: app/templates/order.html:63 +#: application/app/templates/order.html:63 msgid "Sum" msgstr "Summe" -#: app/templates/order.html:73 +#: application/app/templates/order.html:73 msgid "order" msgstr "Bestellen" -#: app/templates/order.html:85 +#: application/app/templates/order.html:85 msgid "Your balance is too low to order a drink." msgstr "Dein Saldo ist zu niedrig um Getränke zu bestellen." -#: app/templates/order.html:86 app/templates/order.html:95 +#: application/app/templates/order.html:86 +#: application/app/templates/order.html:95 +#: application/app/templates/supply.html:54 msgid "back" msgstr "zurück" -#: app/templates/order.html:94 +#: application/app/templates/order.html:94 msgid "This drink is not available." msgstr "Dieses Getränk ist gerade nicht verfügbar." -#: app/templates/registration/logged_out.html:7 +#: application/app/templates/registration/logged_out.html:7 msgid "Drinks - Logged Out" msgstr "Getränke - Abgemeldet" -#: app/templates/registration/logged_out.html:17 +#: application/app/templates/registration/logged_out.html:17 msgid "Logged out! You will be redirected shortly." msgstr "Du wurdest abgemeldet und wirst in Kürze weitergeleitet." -#: app/templates/registration/logged_out.html:19 +#: application/app/templates/registration/logged_out.html:19 msgid "Click here if automatic redirection does not work." msgstr "" "Bitte klicke hier, wenn die automatische Weiterleitung nicht funktioniert." -#: app/templates/registration/login.html:7 +#: application/app/templates/registration/login.html:8 msgid "Drinks - Login" msgstr "Getränke - Anmeldung" -#: app/templates/registration/login.html:26 +#: application/app/templates/registration/login.html:27 msgid "Log in" msgstr "Anmelden" -#: app/templates/registration/login.html:28 +#: application/app/templates/registration/login.html:29 msgid "Password/PIN" msgstr "Passwort/PIN" -#: app/templates/registration/login.html:57 +#: application/app/templates/registration/login.html:58 msgid "login" msgstr "Anmelden" -#: app/templates/registration/login.html:65 +#: application/app/templates/registration/login.html:66 msgid "Choose your account" msgstr "Wähle deinen Account" -#: app/templates/statistics.html:6 +#: application/app/templates/statistics.html:6 msgid "Drinks - Statistics" msgstr "Getränke - Statistiken" -#: app/templates/statistics.html:15 app/templates/userPanel.html:26 +#: application/app/templates/statistics.html:15 +#: application/app/templates/userPanel.html:31 msgid "Statistics" msgstr "Statistiken" -#: app/templates/statistics.html:26 +#: application/app/templates/statistics.html:26 msgid "Your orders per drink" msgstr "Deine Bestellungen pro Getränk" -#: app/templates/statistics.html:30 app/templates/statistics.html:50 +#: application/app/templates/statistics.html:30 +#: application/app/templates/statistics.html:50 msgid "drink" msgstr "Getränk" -#: app/templates/statistics.html:31 app/templates/statistics.html:51 -#: app/templates/statistics.html:71 app/templates/statistics.html:91 -#: app/templates/statistics.html:111 app/templates/statistics.html:131 +#: application/app/templates/statistics.html:31 +#: application/app/templates/statistics.html:51 +#: application/app/templates/statistics.html:71 +#: application/app/templates/statistics.html:91 +#: application/app/templates/statistics.html:111 +#: application/app/templates/statistics.html:131 msgid "count" msgstr "Anzahl" -#: app/templates/statistics.html:46 +#: application/app/templates/statistics.html:46 msgid "All orders per drink" msgstr "Alle Bestellungen pro Getränk" -#: app/templates/statistics.html:66 +#: application/app/templates/statistics.html:66 msgid "Your orders per month (last 12 months)" msgstr "Deine Bestellungen pro Monat (letzte 12 Monate)" -#: app/templates/statistics.html:70 app/templates/statistics.html:90 +#: application/app/templates/statistics.html:70 +#: application/app/templates/statistics.html:90 msgid "month" msgstr "Monat" -#: app/templates/statistics.html:86 +#: application/app/templates/statistics.html:86 msgid "All orders per month (last 12 months)" msgstr "Alle Bestellungen pro Monat (letzte 12 Monate)" -#: app/templates/statistics.html:106 +#: application/app/templates/statistics.html:106 msgid "Your orders per weekday" msgstr "Deine Bestellungen pro Wochentag" -#: app/templates/statistics.html:110 app/templates/statistics.html:130 +#: application/app/templates/statistics.html:110 +#: application/app/templates/statistics.html:130 msgid "day" msgstr "Tag" -#: app/templates/statistics.html:126 +#: application/app/templates/statistics.html:126 msgid "All orders per weekday" msgstr "Alle Bestellungen pro Wochentag" -#: app/templates/userPanel.html:6 app/templates/userPanel.html:8 +#: application/app/templates/supply.html:7 +msgid "Drinks - Supply" +msgstr "Getränke - Beschaffung" + +#: application/app/templates/supply.html:16 +#: application/app/templates/userPanel.html:36 +msgid "Supply" +msgstr "Beschaffung" + +#: application/app/templates/supply.html:27 +msgid "Description" +msgstr "Beschreibung" + +#: application/app/templates/supply.html:32 +msgid "Price" +msgstr "Preis" + +#: application/app/templates/supply.html:42 +msgid "submit" +msgstr "Senden" + +#: application/app/templates/supply.html:53 +msgid "You are not allowed to view this site." +msgstr "Dir fehlt die Berechtigung, diese Seite anzuzeigen." + +#: application/app/templates/userPanel.html:9 +#: application/app/templates/userPanel.html:11 msgid "User" msgstr "Benutzer" -#: app/templates/userPanel.html:12 app/templates/userPanel.html:14 +#: application/app/templates/userPanel.html:15 +#: application/app/templates/userPanel.html:17 msgid "Balance" msgstr "Saldo" -#: app/templates/userPanel.html:22 -msgid "Account" -msgstr "Account" - -#: app/templates/userPanel.html:30 -msgid "Change Password" -msgstr "Passwort ändern" - -#: app/templates/userPanel.html:31 +#: application/app/templates/userPanel.html:24 msgid "Logout" msgstr "Abmelden" -#: app/views.py:47 +#: application/app/templates/userPanel.html:27 +msgid "Account" +msgstr "Account" + +#: application/app/templates/userPanel.html:38 +msgid "Change Password" +msgstr "Passwort ändern" + +#: application/app/views.py:47 msgid "Invalid username or password." msgstr "Benutzername oder Passwort ungültig." diff --git a/static/css/supply.css b/static/css/supply.css new file mode 100644 index 0000000..a149993 --- /dev/null +++ b/static/css/supply.css @@ -0,0 +1,6 @@ +form { + width: 24rem; +} +#supplyPrice { + width: 10rem; +} \ No newline at end of file diff --git a/static/js/supply.js b/static/js/supply.js new file mode 100644 index 0000000..054b08d --- /dev/null +++ b/static/js/supply.js @@ -0,0 +1,56 @@ +document.addEventListener("DOMContentLoaded", () => { + + // elements + + let supply_description = document.getElementById("supplyDescription"); + let supply_price = document.getElementById("supplyPrice"); + + let supply_form = document.getElementById("supplyForm"); + let status_info = document.getElementById("statusInfo"); + let supply_submit_button = document.getElementById("supplySubmitBtn"); + + // custom submit method + + supply_form.addEventListener("submit", (event) => { + + supply_submit_button.disabled = true; + + event.preventDefault(); // Don't do the default submit action! + + if (isNaN(parseFloat(supply_price.value)) || supply_description.value == "") { + status_info.innerText = "Please enter a description and price." + supply_submit_button.disabled = false; + } + + let xhr = new XMLHttpRequest(); + let formData = new FormData(supply_form); + + xhr.addEventListener("load", (event) => { + + status_ = event.target.status; + response_ = event.target.responseText; + + if (status_ == 200 && response_ == "success") { + status_info.innerText = "Success."; + window.location.replace("/"); + } + else { + status_info.classList.add("errorText"); + status_info.innerText = "An error occured."; + window.setTimeout(() => { window.location.reload() }, 5000); + } + + }) + + xhr.addEventListener("error", (event) => { + status_info.classList.add("errorText"); + status_info.innerText = "An error occured."; + window.setTimeout(() => { window.location.reload() }, 5000); + }) + + xhr.open("POST", "/api/supply"); + xhr.send(formData); + + }); + +}) \ No newline at end of file From 9f270c12b485e7ea4a4478fde379406910fbc661 Mon Sep 17 00:00:00 2001 From: W13R <9070224-W13R@users.noreply.gitlab.com> Date: Wed, 2 Nov 2022 21:55:36 +0100 Subject: [PATCH 02/67] Profile pictures are now handled by the application to mitigate possible directory traversals to other sub-directories of the static directory (Admins/Staff with the right to edit user accounts were able to set a path like ../static/favicon.png for the profile picture - this isn't a "i'm in, now i have root access and can hack your mom"-vulnerability, but better fix it before it evolves to one. or a dragon. it's too late for this crap.) --- .gitignore | 4 +++- .../app/templates/registration/login.html | 2 +- application/app/templates/userPanel.html | 2 +- application/app/urls.py | 3 ++- application/app/views.py | 24 +++++++++++++++++++ application/drinks_manager/settings.py | 2 ++ lib/env.sh | 1 + .../default.svg | 0 8 files changed, 34 insertions(+), 4 deletions(-) rename {static/profilepictures => profilepictures}/default.svg (100%) diff --git a/.gitignore b/.gitignore index 8db7fca..4f97b84 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /archive/* /logs/* /packages/* +/profilepictures/* /temp /tmp __pycache__ @@ -12,4 +13,5 @@ __pycache__ !/config/config.sample.sh !/config/Caddyfile !/config/tls/ -!.gitkeep \ No newline at end of file +!/profilepictures/default.svg +!.gitkeep diff --git a/application/app/templates/registration/login.html b/application/app/templates/registration/login.html index af1f25c..765172e 100644 --- a/application/app/templates/registration/login.html +++ b/application/app/templates/registration/login.html @@ -69,7 +69,7 @@