Erklärung Programmiercode

Registriert
Juli 2022
Beiträge
5
Hallo zusammen,

ich soll für ein Projekt nächste Woche ein paar Zeilen Code erklären. Bis jetzt klappts ganz gut, aber an folgender Stelle hab ich Probleme. Ich würde demjenigen, der mir hilft eine 10€ Paysafecard als dankeschön schenken =)
Die # sind meine Kommentare und genauso sollen die Codes alle beschrieben werden. Meine Kommentare dürfen gerne verbessert werden, wenn sie falsch sind :)

Code:
@app.route('/benutzer', methods=['GET', 'POST'])
@login_required
def benutzer():
    if request.method == "GET": #Bei GET Request
        Benutzer_id = session['id']
        cursor = g.con.cursor() #datenbankanfrage an System stellen
        cursor.execute(
            "SELECT Benutzer_id, Benutzername, Vorname, Nachname, Geburtsdatum , Strasse, HausNr, PLZ, Email, TelefonNr"
            " FROM Person WHERE Benutzer_id = %s",
            (Benutzer_id,)) #Sendet Befehl an Datenbank und führt diesen aus
        row = cursor.fetchone() #Datensatz wird gelesen und in der Liste row gespeichert
        Person = dict(Benutzer_id=row[0], Benutzer=row[1], Vorname=row[2], Nachname=row[3], Geburtsdatum=row[4],
                      Strasse=row[5],
                      HausNr=row[6], PLZ=row[7], Email=row[8], TelefonNr=row[9])
        cursor.close()
        return render_template('benutzer.html', Person=Person)


@app.route('/benutzer_bearbeiten/<int:Benutzer_id>', methods=['GET', 'POST'])
@login_required
def benutzer_bearbeiten(Benutzer_id):
    """ Benutzer bearbeiten """
    if request.method == "GET":
        Benutzer_id = session.get('id')
        cursor = g.con.cursor(dictionary=True)
        cursor.execute(
            'SELECT Benutzer_id, Benutzername, Vorname, Nachname, Geburtsdatum, Strasse , HausNr, Person.PLZ, Email,'
            ' TelefonNr, Ort.PLZ, Ort.Stadt, Ort.Land'
            ' FROM Person inner join Ort on Person.PLZ=Ort.PLZ where Benutzer_id = %s ',
            (Benutzer_id,))
        person = cursor.fetchone()
        return render_template('benutzer_bearbeiten.html', person=person)

        # Profiländerung speichern
    cursor = g.con.cursor()
    cur_ort = g.con.cursor()
    cur_ort.execute('SELECT PLZ, Stadt, Land FROM Ort WHERE PLZ = %s', (request.form['PLZ'],))
    plz_object = cur_ort.fetchone()
    if not plz_object:
        cur_ort.execute('INSERT INTO Ort VALUES (%s,%s,%s)',
                        (request.form['PLZ'], request.form['Stadt'], request.form['Land']))
    cursor.execute('UPDATE Person SET Benutzername =%s, Vorname=%s, Nachname=%s, Strasse=%s, HausNr=%s, '
                   'PLZ=%s, Email=%s, TelefonNr=%s WHERE Benutzer_id=%s',
                   (request.form['Benutzername'],
                    request.form['Vorname'],
                    request.form['Nachname'],
                    request.form['Strasse'],
                    request.form['HausNr'],
                    request.form['PLZ'],
                    request.form['Email'],
                    request.form['TelefonNr'],
                    Benutzer_id))
    g.con.commit()
    cursor.close()
    flash("Profil wurde erfolgreich verändert")
    return redirect(url_for('benutzer'))
    return render_template('benutzer_bearbeiten.html', person=person)
 
Zuletzt bearbeitet:
Hilfe zu Hausaufgaben gibt es hier auch gegen Bezahlung nicht.

Also Projekt "erläutern"
 
  • Gefällt mir
Reaktionen: Aduasen und Backfisch
Das ist keine Hausaufgabe, sondern ein freiwiliiges Projekt.
Ich habe über 500 Zeilen Code beschrieben und dieser hier macht mir lediglich zu schaffen. :)
 
Wäre schön wenn du wenigstens ein Mindestmaß an Eigenleistung erbringen könntest und z.B. die Quellcode-Funktion des Forums nutzen würdest oder versuchst, uns zu sagen, was genau du nicht verstehst.
 
  • Gefällt mir
Reaktionen: Backfisch
Hausaufgabe hin oder her, pack den Code bitte in [CODE][/CODE] Klammern
 
Vulpecula schrieb:
Wäre schön wenn du wenigstens ein Mindestmaß an Eigenleistung erbringen könntest und z.B. die Quellcode-Funktion des Forums nutzen würdest oder versuchst, uns zu sagen, was genau du nicht verstehst
Stimmt! Hab ich angepasst.
Nilson schrieb:
Hausaufgabe hin oder her, pack den Code bitte in [CODE][/CODE] Klammern
Ist vollbracht.
 
Sorry, aber solche Kommentare sind eigentlich nur überflüssig (und es erläutert so ziemlich rein gar nichts)...
Code:
if request.method == "GET": #Bei GET Request

Natürlich kann es sinnvoll sein, zu kommentieren, was man macht (auch wenn es dafür meist noch bessere Wege als Kommentare gibt), aber viel wichtiger ist es häufig das Warum zu dokumentieren. Das Was geht hoffentlich aus dem Code vor.

Ich meine jemand, der diesen Ausdruck if request.method == "GET" nicht versteht, der sollte auch besser die Finger vom Code lassen.

Und apropos Sinn, nehmen wir Zeile 24. Das Was ist klar, das Warum wirft bei mir Rätsel auf.
Code:
Benutzer_id = session.get('id')
Wird die Benutzer_id nicht als GET-Parameter übergeben? Und warum wird sie dann nur beim GET-Request aus der Session genommen, nicht aber beim POST?

Welchen Sinn hat Zeile 57?
 
Zuletzt bearbeitet:
Sieht schon ganz gut aus, man kann glaub auch der Forensoftware Syntax highlighting beibringen.

Meine Kommentare: Der Code ist banal genug, dass man quasi keine Kommentare braucht (IMHO). Ich bin eher wegen des Codes etwas überrascht :D.

Z. 12f stopft die Abfrage in ein dict, Z.25 und Z.32 suggeriert, dass man sich das erspart, wenn man dicitionary=true setzt beim cursor.

Z. 57 sieht mir unreachable aus (bin jetzt kein Python Crack, aber ist das reachable?)

Geschmackssache: Ich würde ja bei Benutzer-Bearbeiten ins if die Bearbeitung packen, das erscheint mir irgendwie logisch (denke mal na HEAD und Co, wenn du das mal willst, ist POST der Spezialfall für den Endpoint).

Cursor.execute macht Prepared statements oder ist das ein "printf"-like String Substitution mit dem %s? Wenn ja: SQL Injection vuln.

benutzer_bearbeiten hat zwei SQL Statements, die am Ende committed werden, ist die DB automatisch in ner Transaction, wenn das aufgerufen wird? Oder fehlt da ein beginTransaction (oder ähnliches)?


===> Das sind die Sachen, die mir aufgefallen sind. Das ist das, was kommentierwürdig ist. Im Kommentar steht das, was nicht im Code steht (da implizit, Annahme, ...).

EDIT: Insert into Ort: Wie verhinderst du, dass die Tabelle zugespammt wird, mit nem UNIQUE wird das ganze ja beim commit failen, ohne wird bei jedem Edit ne Zeile angelegt (mit Duplikaten noch und nöcher)?
 
  • Gefällt mir
Reaktionen: Millkaa, jodakaa und madmax2010
Ich frag mich eher, wie verhindert wird, dass die Benutzer sich nicht gegenseitig bearbeiten... darüber mache ich mir gerade noch viel mehr Sorgen als über SQL-Injections :-)
 
  • Gefällt mir
Reaktionen: [ChAoZ] und madmax2010
Was genau verstehst du dort nicht?

Also generell ist es ja immer gleich:
Das Formular wird abgeschickt, GET-Request zum abrufen von Daten, POST-Request zum mitschicken von Daten.
Dann ist ja immer vorgesehen, dass der Nutzer eingeloggt ist, also gibt es eine SessionId anhand der Nutzer scheinbar auch identifiziert wird.
Bei der ersten Funktion ist es dann einfach nur eine Datenbankabfrage anhand der SessionId/BenutzerId, die mit dem SELECT Befehl die Nutzerdaten aus der Tabelle abruft die für den Benutzer gespeichert sind.
Dabei ist es auch immer gleich, cursor holen, Befehl ausführen, dann Ergebnis fetchen, Die Daten ins richtige Format bringen und wieder rendern/ausgeben.

Bei der zweiten Funktion geht es darum Benutzerdaten zu ändern, zunächst ist es ähnlich, da wird im GET Fall der Ort des Benutzers mit abgefragt, der in einer anderen Tabelle mit drin steht und beide Einträge sind miteinander verknüpft (inner join).
Im POST Fall wird zunächst mit dem SELECT Befehl geschaut ob der Ort in der DB existiert und wenn nein, wird er mit dem darauf folgenden INSERT eingetragen.
Erst danach werden mit dem UPDATE Befehl die Benutzerdaten geändert/aktualisiert.

Das in den Zeilen zu kommentieren kriegst du denke damit selbst hin, halte auch nicht so viel davon jede Zeile zu beschreiben, aber irgendeinen Sinn wird es ja evtl. haben.

Ansonsten sind konkretere Fragen besser, wo es genau hakt.
 
tollertyp schrieb:
Ich frag mich eher, wie verhindert wird, dass die Benutzer sich nicht gegenseitig bearbeiten... darüber mache ich mir gerade noch viel mehr Sorgen als über SQL-Injections :-)
Na ja, die ID kommt ja aus der Session, wenn die funktioniert wie in PHP, dann KANN das schon sicher sein.

Genau zu dem Thema: Was ist der Unterschied zwischen session.get('id') und session['id']? Warum einmal so und das andere Mal anders?
 
@Hancock:
Und warum wird dann beim impliziten POST die Benutzer-ID aus dem Request-Parameter ungeprüft verwendet? Sehr sicher...

Edit:
Und ehrlich gesagt bin ich schockiert, dass du das selbst nach meinem Hinweis nicht siehst. (Ich weiß, du bist nicht der TE, aber du bescheinigst dem ja "Sicherheit")
 
Zuletzt bearbeitet:
Grundsätzlich sieht mir das auch nach Hausarbeit aus. Daher keine all zu spezifische Hilfe. Deine Kommentare sind zu großen Teilen falsch. Sie haben zwar grob etwas mit dem zu tun, was im Quelltext steht, sind aber konstant in einer Art falsch, die darauf deuten, dass da Verständnis fehlt, was mich wiederum vermuten lässt. dass da Code zusammen kopiert aber nicht verstanden wurde.

Beispiel, meine Kommentare mit ** markiert, in der je folgenden Zeile
Code:
[...]
 if request.method == "GET": #Bei GET Request
** Es wird geprüft, ob der Inhalt von request.method "GET" ist. "Bei GET Request" könnte derart interpretiert werden, dass hier irgendwas triggert, dem ist jedoch nicht der Fall. Wobei je nach Ausgang der Prüfung gesprungen wird..

        Benutzer_id = session['id']
        cursor = g.con.cursor() #datenbankanfrage an System stellen
** Jenachdem was g.con.cursor() macht, wird hier höchstwahrscheinlich keine Abfrage gestellt. Was wirklich passiert kann dir nur die Methodendefinition von cursor() verraten
        cursor.execute(
            "SELECT Benutzer_id, Benutzername, Vorname, Nachname, Geburtsdatum , Strasse, HausNr, PLZ, Email, TelefonNr"
            " FROM Person WHERE Benutzer_id = %s",
            (Benutzer_id,)) #Sendet Befehl an Datenbank und führt diesen aus
** Hier passiert viel mehr, es wird ein SQL Statement mit Daten aus einer Variablen komplementiert, der Datenbank gegenüber als Cursor definiert. Ganz nebenbei ist das richtig beschissenes Design. Bitte nutze für sowas prepared Statements! Solche Kostrukte sind quasi garantierte SQL-Injections von bösen Buben und Mädels
        row = cursor.fetchone() #Datensatz wird gelesen und in der Liste row gespeichert
** Bei dem gezeigten Code, wäre für mich nicht erkennbar, dass row ne Liste sein soll. Wenn g.con.cursor() wirklich einen Cursor der DB definiert, liest cursor.fetchone() auch keinen Datensatz aus dem Kontext deines Programms.
[...]
Edit:
Wobei so detailliertes Kommentieren meist sowieso kaum etwas bringt. Mit etwas Übung sieht man ja, was jede Zeile Code macht. Was aber nur der erste Schritt ist um zu verstehen was Programme machen.
 
  • Gefällt mir
Reaktionen: Backfisch, madmax2010 und snickii
Hancock schrieb:
EDIT: Insert into Ort: Wie verhinderst du, dass die Tabelle zugespammt wird, mit nem UNIQUE wird das ganze ja beim commit failen, ohne wird bei jedem Edit ne Zeile angelegt (mit Duplikaten noch und nöcher)?
Warum wird bei jedem Edit eine neue Zeile angelegt?
Doch nur, wenn die PLZ unbekannt ist...
 
Superior1337 schrieb:
Was genau verstehst du dort nicht?

Also generell ist es ja immer gleich:
Das Formular wird abgeschickt, GET-Request zum abrufen von Daten, POST-Request zum mitschicken von Daten.
Dann ist ja immer vorgesehen, dass der Nutzer eingeloggt ist, also gibt es eine SessionId anhand der Nutzer scheinbar auch identifiziert wird.
Bei der ersten Funktion ist es dann einfach nur eine Datenbankabfrage anhand der SessionId/BenutzerId, die mit dem SELECT Befehl die Nutzerdaten aus der Tabelle abruft die für den Benutzer gespeichert sind.
Dabei ist es auch immer gleich, cursor holen, Befehl ausführen, dann Ergebnis fetchen, Die Daten ins richtige Format bringen und wieder rendern/ausgeben.

Bei der zweiten Funktion geht es darum Benutzerdaten zu ändern, zunächst ist es ähnlich, da wird im GET Fall der Ort des Benutzers mit abgefragt, der in einer anderen Tabelle mit drin steht und beide Einträge sind miteinander verknüpft (inner join).
Im POST Fall wird zunächst mit dem SELECT Befehl geschaut ob der Ort in der DB existiert und wenn nein, wird er mit dem darauf folgenden INSERT eingetragen.
Erst danach werden mit dem UPDATE Befehl die Benutzerdaten geändert/aktualisiert.

Das in den Zeilen zu kommentieren kriegst du denke damit selbst hin, halte auch nicht so viel davon jede Zeile zu beschreiben, aber irgendeinen Sinn wird es ja evtl. haben.

Ansonsten sind konkretere Fragen besser, wo es genau hakt.
Danke für deine Hilfe!

Code:
Person = dict(Benutzer_id=row[0], Benutzer=row[1], Vorname=row[2], Nachname=row[3], Geburtsdatum=row[4],
              Strasse=row[5],
              HausNr=row[6], PLZ=row[7], Email=row[8], TelefonNr=row[9])
was wird hier genau gemacht?
 
wassermelone_74 schrieb:
was wird hier genau gemacht?
Da werden die Einträge welche aus der Datenbank ( gespeichert in row) gelesen wurden in ein Dictionary mit dem Namen "Person" übernommen.

Der Name ist halt Mist in Anbetracht, dass man bereits andere Objekte mit "person" benannt hat und die Datenbanktabelle aus der abgerufen wurden auch schon "Person" hieß.

Verwendet wird das in einem return, warum der genau ein dict braucht oder ob das später nochmal genutzt wird wo der Zugriff auf row nicht auch gereicht hätte ist aus dem Schnipsel nicht ersichtlich.
 
Zuletzt bearbeitet:
Code:
@app.route('/benutzer', methods=['GET', 'POST'])
@login_required
def benutzer():
    if request.method == "GET": #Bei GET Request
        Benutzer_id = session['id'] #Session ID ahnand Nutzer identifieziert wird, es ist vorgesehen das Nutzer eingeloggt ist
        cursor = g.con.cursor() #Datenbankabfrage, anhand der Benutzer ID, die mit dem SELECT befehl die Nutzerdaten aus der Tabelle abruft,*
        cursor.execute(         #*die für den Benutzer gespeichert sind
            "SELECT Benutzer_id, Benutzername, Vorname, Nachname, Geburtsdatum , Strasse, HausNr, PLZ, Email, TelefonNr"
            " FROM Person WHERE Benutzer_id = %s",
            (Benutzer_id,))
        row = cursor.fetchone()
        Person = dict(Benutzer_id=row[0], Benutzer=row[1], Vorname=row[2], Nachname=row[3], Geburtsdatum=row[4], #DICT=Einträge welche aus der Datenbank gelesen wurden, werden in ein Dictionary*
                      Strasse=row[5],                                                                            #*mit dem Namen 'Person' übernommen. Einträge sind gespeichert in row.
                      HausNr=row[6], PLZ=row[7], Email=row[8], TelefonNr=row[9]) #Cursor wird geholt, Befehl wird ausgeführt, Eregbnis wird geholt, Daten werden wieder*
                                                                                 #*ins richtige Format gebracht und ausgegeben
        cursor.close()


@app.route('/benutzer_bearbeiten/<int:Benutzer_id>', methods=['GET', 'POST']) #es geht darum benutzerdaten zu ändern
@login_required
def benutzer_bearbeiten(Benutzer_id):
    """ Benutzer bearbeiten """
    if request.method == "GET":
        Benutzer_id = session.get('id') #Session ID ahnand Nutzer identifieziert wird, es ist vorgesehen das Nutzer eingeloggt ist
        cursor = g.con.cursor(dictionary=True)
        cursor.execute(
            'SELECT Benutzer_id, Benutzername, Vorname, Nachname, Geburtsdatum, Strasse , HausNr, Person.PLZ, Email,'
            ' TelefonNr, Ort.PLZ, Ort.Stadt, Ort.Land'
            ' FROM Person inner join Ort on Person.PLZ=Ort.PLZ where Benutzer_id = %s ', #Der Ort des Benutzers wird mit abgefragt, der in einer anderen
            (Benutzer_id,))                                                              #*Tabelle mit drin steht. Beide Einträge sind miteinander verknüft (inner join)
        person = cursor.fetchone()
        return render_template('benutzer_bearbeiten.html', person=person)

        # Profiländerung speichern
    cursor = g.con.cursor()
    cur_ort = g.con.cursor()
    cur_ort.execute('SELECT PLZ, Stadt, Land FROM Ort WHERE PLZ = %s', (request.form['PLZ'],)) #Mit dem Select Befehl wird geschaut ob der Ort in der DB existiert.
    plz_object = cur_ort.fetchone()
    if not plz_object:
        cur_ort.execute('INSERT INTO Ort VALUES (%s,%s,%s)',
                        (request.form['PLZ'], request.form['Stadt'], request.form['Land'])) #Wenn Ort in DB nicht existiert, wird er mit dem darauffolgendem INSERT eingetragen
    cursor.execute('UPDATE Person SET Benutzername =%s, Vorname=%s, Nachname=%s, Strasse=%s, HausNr=%s, ' #Erst danach werden mit dem UPDATE Befehl*
                   'PLZ=%s, Email=%s, TelefonNr=%s WHERE Benutzer_id=%s',                                  #*die Benutzerdaten geändert/aktualisiert.
                   (request.form['Benutzername'],
                    request.form['Vorname'],
                    request.form['Nachname'],
                    request.form['Strasse'],
                    request.form['HausNr'],
                    request.form['PLZ'],
                    request.form['Email'],
                    request.form['TelefonNr'],
                    Benutzer_id))
    g.con.commit()
    cursor.close()
    flash("Profil wurde erfolgreich verändert")
    return redirect(url_for('benutzer'))
    return render_template('benutzer_bearbeiten.html', person=person)


Mit eurer Hilfe sieht der Code nun so kommentiert aus! Stimmen die Kommentarebis hierhin?

Nochmal danke an jeden einzelnen!
 
Es ergibt nach wie vor keinen Sinn mit der Benutzer-ID und der Session.

Es wird eine Route definiert in Zeile 19
Code:
@app.route('/benutzer_bearbeiten/<int:Benutzer_id>', methods=['GET', 'POST'])
@login_required
def benutzer_bearbeiten(Benutzer_id):
Diese Route enthält eiinen Parameter vom Typ Integer mit dem Namen Benutzer_id, natürlich kein Request-Parameter wie ich vorher fälschlich schrieb. Dieser Wert wird als Parameter in die Funktion benutzer_bearbeiten übergeben.
Das heißt, Benutzer 0815 ruft z.B https://meine.unsichere.seite.de/benutzer_bearbeiten/0815 auf, um seine Daten zu bearbeiten. Wobei es eigentlich für den GET-Request vollkommen egal ist, welche Benutzer-ID er da verwendet, denn in Zeile 24 wird sie eh durch die in der Session hinterlegten Benutzer-ID überschrieben (der Vollständigkeit halber inkl. Zeile 23):
Code:
    if request.method == "GET":
        Benutzer_id = session.get('id')

So, beim POST passiert das nun aber nicht. Dort wird dann stur die Benutzer-ID verwendet, wie über die URL an den Server geschickt wird.

What couild possibly go wrong? Nun, was hindert nun Benutzer 0815 einfach den POST-Request an eine andere URL zu senden, z.B. https://meine.unsichere.seite.de/benutzer_bearbeiten/4711
Und schon hat Benutzer 0815 unerlaubt die Daten von 4711 geändert.

Deshalb meine Fragen:
Warum braucht es den URL-Parameter Benutzer-ID überhaupt, wenn der Benutzer eh nur sich selbst bearbeiten kann? Warum wird beim GET die Benutzer-ID aus der Session, beim POST allerdings die aus der URL verwendet?
 
  • Gefällt mir
Reaktionen: maloz
tollertyp schrieb:
@Hancock:
Und warum wird dann beim impliziten POST die Benutzer-ID aus dem Request-Parameter ungeprüft verwendet? Sehr sicher...

Edit:
Und ehrlich gesagt bin ich schockiert, dass du das selbst nach meinem Hinweis nicht siehst. (Ich weiß, du bist nicht der TE, aber du bescheinigst dem ja "Sicherheit")
Ahh, ja du hast recht. Auch gut, dass da die Variablen einmal als Parameter und dann überschrieben (im GET if ) also Variable verwendet wird...

tollertyp schrieb:
Warum wird bei jedem Edit eine neue Zeile angelegt?
Doch nur, wenn die PLZ unbekannt ist...
Gut beobachtet, ich hab die Einrückung übersehen... Ja, das wird geprüft. Gleichzeitig aber es wird nicht validiert, dass es eine gültige PLZ ist (also 5 Zeichen in DE). Und ich kann dem Ort nen Namen geben (12345 Foobar) und dann ist der das auf ewig, auch für alle anderen Nutzer. Ich kann also als Benutzer alle PLZs durchgehen und alle nicht-PLZs (z.B. 1BERLIN2, oder 123456, oder 0000000).

@wassermelone_74 : Na ja, du erklärst, was der Code tut nochmal. Warum er das tut aber nicht. (Wir können dir auch nicht sagen, warum, da wir den Rest des Codes nicht kennen). Mglw. ist das dein Arbeitspaket, dann well done. Sinnvoll ist das aber mMn. nicht.
 
Zurück
Oben