Add attachment support for forms

This commit is contained in:
Yannik Rödel 2022-03-17 13:45:08 +01:00
parent 5b84a20963
commit c7aefe142d
6 changed files with 31 additions and 17 deletions

1
.gitignore vendored
View file

@ -19,3 +19,4 @@ _site/
# Private environments in the HTTP playground folder # Private environments in the HTTP playground folder
/playground/*.private.env.json /playground/*.private.env.json
/httpd.dev.conf

View file

@ -1,12 +1,14 @@
#!/usr/bin/env python #!/usr/bin/env python
import base64
import io import io
import cgi import cgi
import collections import collections
import hmac import hmac
import mimetypes
import os import os
import secrets import secrets
from typing import Any, Optional, overload from typing import Any, Optional, overload, IO
import itsdangerous import itsdangerous
import requests import requests
@ -90,7 +92,7 @@ def get_form_value(name: str, default: Optional[str], cast: type[str] = str) ->
@overload @overload
def get_form_value(name: str, default: Optional[int], cast: type[int]) -> int:... def get_form_value(name: str, default: Optional[int], cast: type[int]) -> int:...
@overload @overload
def get_form_value(name: str, default: None = ..., cast: type[io.BytesIO] = ...) -> io.BytesIO:... def get_form_value(name: str, default: None = ..., cast: type[bytes] = ...) -> tuple[str, bytes]:...
def get_form_value( def get_form_value(
name: str, name: str,
default: Any = None, default: Any = None,
@ -102,11 +104,14 @@ def get_form_value(
else: else:
return default return default
if cast is io.BytesIO: if cast is bytes:
value_object = form[name] value_object = form[name]
if isinstance(value_object, list) or not value_object.file: if (
isinstance(value_object, list)
or not value_object.file
):
fail("400 Bad Request", f"Invalid value for field: {name}") fail("400 Bad Request", f"Invalid value for field: {name}")
return io.BytesIO(value_object.file) return (value_object.filename or "upload"), value_object.file.read()
else: else:
try: try:
return cast(form.getfirst(name)) return cast(form.getfirst(name))
@ -126,6 +131,7 @@ if not hmac.compare_digest(csrf_token, given_csrf_token):
contact_name = get_form_value("contactname") contact_name = get_form_value("contactname")
contact_email = get_form_value("contactemail") contact_email = get_form_value("contactemail")
message = get_form_value("message") message = get_form_value("message")
attachment: Optional[tuple[str, bytes]] = None
ticket_details = collections.OrderedDict() ticket_details = collections.OrderedDict()
ticket_details["Kontaktperson"] = contact_name ticket_details["Kontaktperson"] = contact_name
@ -159,7 +165,7 @@ match request_uri:
ticket_details["Adresse"] = get_form_value("addressline") ticket_details["Adresse"] = get_form_value("addressline")
ticket_details["PLZ"] = get_form_value("postalcode") ticket_details["PLZ"] = get_form_value("postalcode")
ticket_details["Stadt"] = get_form_value("city") ticket_details["Stadt"] = get_form_value("city")
#document = get_form_value("document") attachment = get_form_value("document")
# Note: the actual form contains a checkbox for whether the user has # Note: the actual form contains a checkbox for whether the user has
# read the guidelines, but we don't actually bother checking that here. # read the guidelines, but we don't actually bother checking that here.
@ -167,7 +173,7 @@ match request_uri:
case "/hardware-spenden/organisation": case "/hardware-spenden/organisation":
form_name = "Hardwarespende (Organisation)" form_name = "Hardwarespende (Organisation)"
ticket_details["Organisation"] = get_form_value("organization") ticket_details["Organisation"] = get_form_value("organization")
#inventory = get_form_value("inventory") attachment = get_form_value("inventory", cast=bytes)
case "/hardware-spenden/privat/laptop": case "/hardware-spenden/privat/laptop":
form_name = "Laptopspende (privat)" form_name = "Laptopspende (privat)"
@ -199,9 +205,14 @@ try:
type="web", type="web",
content_type="text/plain", content_type="text/plain",
subject=f"Kontaktformular-Anfrage {contact_name}", subject=f"Kontaktformular-Anfrage {contact_name}",
body=message body=message,
) attachments=[] if attachment is None else [{
) "filename": attachment[0],
"data": base64.b64encode(attachment[1]).decode(),
"mime-type": mimetypes.guess_type(attachment[0])[0] or "text/plain",
}],
),
),
) )
response.raise_for_status() response.raise_for_status()
ticket_id = response.json()["id"] ticket_id = response.json()["id"]
@ -238,6 +249,6 @@ try:
print("Content-Type: text/html") print("Content-Type: text/html")
print("Location: /kontakt/fertig") print("Location: /kontakt/fertig")
print("") print("")
except: except Exception as e:
fail("500 Internal Server Error", "Backend error") fail("500 Internal Server Error", str(e))

View file

@ -53,7 +53,6 @@
config = { config = {
Env = [ Env = [
"SITE_DIRECTORY=${packages.site}"
# We need to provide these default variables because otherwise # We need to provide these default variables because otherwise
# lighttpd doesn't even parse its configuration file: # lighttpd doesn't even parse its configuration file:
"ZAMMAD_URL=https://ticket.z31.it" "ZAMMAD_URL=https://ticket.z31.it"
@ -78,6 +77,7 @@
buildInputs = [ pkgs.makeWrapper ]; buildInputs = [ pkgs.makeWrapper ];
paths = [ paths = [
pkgs.lighttpd
nodejs nodejs
nodeDependencies nodeDependencies
python python
@ -91,7 +91,7 @@
shellHook = '' shellHook = ''
export NODE_PATH=${nodeDependencies}/lib/node_modules export NODE_PATH=${nodeDependencies}/lib/node_modules
export PATH="${nodeDependencies}/bin:${nodejs}/bin:${pkgs.nodePackages.npm-check-updates}/bin:${python}/bin:$PATH" export PATH="${pkgs.lighttpd}/bin:${nodeDependencies}/bin:${nodejs}/bin:${pkgs.nodePackages.npm-check-updates}/bin:${python}/bin:$PATH"
echo "" echo ""
echo " To start editing content, run:" echo " To start editing content, run:"

View file

@ -32,7 +32,7 @@ $HTTP["url"] =~ "^/cgi-bin/" {
cgi.x-sendfile-docroot = ( "@site@" ) cgi.x-sendfile-docroot = ( "@site@" )
setenv.set-environment = ( setenv.set-environment = (
"SITE_DIRECTORY" => env.SITE_DIRECTORY, "SITE_DIRECTORY" => "@site@",
"ZAMMAD_URL" => env.ZAMMAD_URL, "ZAMMAD_URL" => env.ZAMMAD_URL,
"ZAMMAD_TOKEN" => env.ZAMMAD_TOKEN, "ZAMMAD_TOKEN" => env.ZAMMAD_TOKEN,
"ZAMMAD_GROUP" => env.ZAMMAD_GROUP "ZAMMAD_GROUP" => env.ZAMMAD_GROUP

View file

@ -5,7 +5,7 @@ useForms: true
# Privat einen Computer beantragen # Privat einen Computer beantragen
<form method="post" action="/computer-beantragen/privat"> <form method="post" action="/computer-beantragen/privat" enctype="multipart/form-data">
Auf dieser Seite kannst du einen Antrag einreichen, um einen Computer von uns zu Auf dieser Seite kannst du einen Antrag einreichen, um einen Computer von uns zu
erhalten. Bitte teile uns zunächst deine Kontaktdaten mit. erhalten. Bitte teile uns zunächst deine Kontaktdaten mit.

View file

@ -5,7 +5,7 @@ useForms: true
# Hardware als Organisation spenden # Hardware als Organisation spenden
<form method="post" action="/hardware-spenden/organisation"> <form method="post" action="/hardware-spenden/organisation" enctype="multipart/form-data">
Vielen Dank, dass du uns deine gebrauchte Hardware überlassen möchtest! Um die Vielen Dank, dass du uns deine gebrauchte Hardware überlassen möchtest! Um die
Übergabe möglichst reibungslos zu gestalten, benötigen wir zunächst ein paar Übergabe möglichst reibungslos zu gestalten, benötigen wir zunächst ein paar
@ -59,11 +59,13 @@ _SSD_-Festplatten werden in einem standardisierten Vorgehen über den Terminal g
Das Bundesamt für Sicherheit in der Informationstechnik (BSI) empfiehlt für die Datenlöschung ebenfalls **DBAN** oder vergleichbare Software. Weitere Informationen zum Thema findest du auf der Website des [BSI](https://www.bsi-fuer-buerger.de/BSIFB/DE/Empfehlungen/RichtigLoeschen/richtigloeschen_node.html). Das Bundesamt für Sicherheit in der Informationstechnik (BSI) empfiehlt für die Datenlöschung ebenfalls **DBAN** oder vergleichbare Software. Weitere Informationen zum Thema findest du auf der Website des [BSI](https://www.bsi-fuer-buerger.de/BSIFB/DE/Empfehlungen/RichtigLoeschen/richtigloeschen_node.html).
Es gibt vier gängige Lösungen, die Datenschutzanforderungen deines Unternehmens mit einer Spende an Angestöpselt in Einklang zu bringen: Es gibt vier gängige Lösungen, die Datenschutzanforderungen deines Unternehmens mit einer Spende an Angestöpselt in Einklang zu bringen:
- Löschung im Unternehmen (gut für uns): Ihr übernehmt die Löschung bzw. das Überschreiben der Daten. Das installieren von Windows ist nicht notwendig. Im Verein installieren wir dann Ubuntu. - Löschung im Unternehmen (gut für uns): Ihr übernehmt die Löschung bzw. das Überschreiben der Daten. Das installieren von Windows ist nicht notwendig. Im Verein installieren wir dann Ubuntu.
- Löschung im Verein (gängigste Variante): Ihr überlasst uns Computer + Festplatten auf Vertrauensbasis. Wir überschreiben alle Inhalte nach obiger Methode und installieren Ubuntu. Defekte HDD/SSDs vernichten wir mechanisch vor dem Recycling. - Löschung im Verein (gängigste Variante): Ihr überlasst uns Computer + Festplatten auf Vertrauensbasis. Wir überschreiben alle Inhalte nach obiger Methode und installieren Ubuntu. Defekte HDD/SSDs vernichten wir mechanisch vor dem Recycling.
- Zertifizierte Löschung bei unserem [Kooperationspartner](https://bb-net.de/) (ab ca. 100 Geräten möglich): Die gespendeten Geräte liefert ihr an bb-net media GmbH in Schweinfurt. Dort werden die Festplatten zertifiziert gelöscht. Unser Kooperationspartner behält entweder einen Teil der Geräte zur Kostendeckung oder stellt euch klassisch eine Rechnung für die Löschung aus. - Zertifizierte Löschung bei unserem [Kooperationspartner](https://bb-net.de/) (ab ca. 100 Geräten möglich): Die gespendeten Geräte liefert ihr an bb-net media GmbH in Schweinfurt. Dort werden die Festplatten zertifiziert gelöscht. Unser Kooperationspartner behält entweder einen Teil der Geräte zur Kostendeckung oder stellt euch klassisch eine Rechnung für die Löschung aus.
## Weitere Informationen ## Weitere Informationen
<dl> <dl>
<dt>Spendenquittungen</dt> <dt>Spendenquittungen</dt>
<dd> <dd>