From 73ef9bcfaa0296f1c1e0e4e254d2032a68e8093b Mon Sep 17 00:00:00 2001 From: "Jorge E. Gomez" Date: Thu, 28 Jul 2022 01:42:30 -0500 Subject: [PATCH] Initial version --- README.md | 16 +++++++++ __pycache__/main.cpython-310.pyc | Bin 0 -> 663 bytes database.db | Bin 0 -> 12288 bytes html/index.html | 10 ++++++ init_db.py | 12 +++++++ main.py | 60 +++++++++++++++++++++++++++++++ requirements.txt | 10 ++++++ schema.sql | 9 +++++ templates/template.html | 18 ++++++++++ templates/thanks.html | 23 ++++++++++++ 10 files changed, 158 insertions(+) create mode 100644 README.md create mode 100644 __pycache__/main.cpython-310.pyc create mode 100644 database.db create mode 100644 html/index.html create mode 100755 init_db.py create mode 100755 main.py create mode 100644 requirements.txt create mode 100644 schema.sql create mode 100644 templates/template.html create mode 100644 templates/thanks.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..f6ae5ce --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Sample application for verifying attendance through a unique QR code + +To run in a Python virtual environment, using **Flask**: + +```bash +mkvenv flask +venv flask +pip install Flask Flask-QRcode Flask-UUID +``` + +To create an empty `database.db` sqlite file with the table described in +`schema.sql`: + +```bash +./init_db.py +``` diff --git a/__pycache__/main.cpython-310.pyc b/__pycache__/main.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad3d7ab8d25f7463510e431d603c1db794289578 GIT binary patch literal 663 zcmYjNy^a$x5cb%+o6WBdNPSAX1zACfq7w*-@(LoFG%K^7B%ACXwim%FR7ZPHa6(E- z9)cHf2^Ft!4cL1Jy0Kv3Rpow zSF~U&1_S{;K?S>}D6TU@NSEWMZZFP}HV8%*wou$dT$_2_>h>cfs zy*0$@pnuDdhXL72^4vtjN z6MO;91UtYD5#E8^kZ;}<0MXvj7yb#BBnmFu;hKNU(yE+W(+=J#luuu6u+;5vSru57 zIIGH8JDlAULosUySt;<-yyF!}W?P%y7P)1m4BMbKer*|O!+2erGliu{{&CN8jHnD9 zgf{#;k&mo{{(z`Dyd4WkHCoc45)o8p!Guf2gAP>yr4znmK(d2mH_O9I1 Sl#b{b4IzXHguw{BKYsxUaI2>P literal 0 HcmV?d00001 diff --git a/database.db b/database.db new file mode 100644 index 0000000000000000000000000000000000000000..3b93c22a0d9e5d89af941603917f5f545ac1b644 GIT binary patch literal 12288 zcmeI2O>Wab6oBoxr1=kT`_re9t&x28!t`bo4aSoTXNDy1A;toPU+>Gj{0}I!l7l24KIwn zh2>*@dncFU-@jhn^Nlx~`Ym#++8;sr1Y+U2sNS@VW5Yr%tKO(t9rWDjpjz8Ltv6Xw z!)V%jtIYm{Iy34byWTL)>{_FRnx{5uwoMZ~GmdI)(?*AF%VPQ6-zmQ;=X3TH+c;<0 z*A>^}Q#!dlo+$Wh^Gxgmm%Yk-&y3Pv)4kM})XU`O>}|eg{;d& zmz6Ln;%g`*g0YKfMe}DTsczUsERaIfi4rEo_Bsk2CPg%mg1s*)VNyH@qL4Hl%VAP% z-3togUQk2=DO6nvbzbo6DA;EwtoMb#{^zqlIJQB60WbgtzyKHk17H9QfB`T72EYIq R00aM(ffz63c>k}2%uhOvL2Up4 literal 0 HcmV?d00001 diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..f70f774 --- /dev/null +++ b/html/index.html @@ -0,0 +1,10 @@ + + + Test + + +

Hi!

+

OK.

+ + + diff --git a/init_db.py b/init_db.py new file mode 100755 index 0000000..29e3d58 --- /dev/null +++ b/init_db.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 + +import sqlite3 + +connection = sqlite3.connect('database.db') + +with open('schema.sql') as f: + connection.executescript(f.read()) + +connection.commit() +connection.close() + diff --git a/main.py b/main.py new file mode 100755 index 0000000..7b93cce --- /dev/null +++ b/main.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +import uuid +import sqlite3 +from datetime import datetime +from flask import Flask, request, render_template +from flask_uuid import FlaskUUID +from flask_qrcode import QRcode +# TODO: use sockets to broadcast when a hit to the expected uuid has been +# received, so that connected clients (the Chromium public browser) can +# refresh the qr code and update the list of users +# https://flask-socketio.readthedocs.io + +app = Flask(__name__, static_folder='html') +FlaskUUID(app) +QRcode(app) + +def get_db_connection(): + conn = sqlite3.connect('database.db') + conn.row_factory = sqlite3.Row + return conn + +@app.route('/') +def show_qr_and_list(): + # TODO: reject direct connections to server; allow access only via proxy + conn = get_db_connection() + data = conn.execute('SELECT * FROM hits ORDER BY id DESC LIMIT 10').fetchall() + conn.close() + + # TODO: next_ uuid should be in global state, to verify it when it's received + return render_template("template.html", + next_uuid=str(uuid.uuid1()), + hits=data) + +@app.route('/') +def catch_uuids(id): + ua = request.headers.get('User-Agent') + # TODO: use the Remote-User header that Authelia should set after authentication + # (check if "Authorization" in request.headers, or request.authorization) + time = datetime.now().strftime("%A %Y-%m-%d %H:%M:%S") + error = None + conn = get_db_connection() + existing = conn.execute( + 'SELECT * FROM hits WHERE uuid = ?', (str(id),)).fetchone() + # TODO: verify that the uuid was generated by us (otherwise any uuid, + # like one generated by the user, would be accepted) + if existing: + error = 'ALREADY_USED' + else: + data = conn.execute( + 'SELECT * FROM hits WHERE user = ? ORDER BY id DESC LIMIT 10', (ua,) + ).fetchall() + conn.execute("INSERT INTO hits (uuid, user) VALUES (?, ?)", (str(id), ua)) + conn.commit() + conn.close() + + return render_template('thanks.html', user=ua, time=time, error=error, hits=data) + +if __name__ == '__main__': + app.run() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a27bddb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +click==8.1.3 +Flask==2.1.3 +Flask-QRcode==3.1.0 +Flask-UUID==0.2 +itsdangerous==2.1.2 +Jinja2==3.1.2 +MarkupSafe==2.1.1 +Pillow==9.2.0 +qrcode==7.3.1 +Werkzeug==2.2.1 diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..961f533 --- /dev/null +++ b/schema.sql @@ -0,0 +1,9 @@ +DROP TABLE IF EXISTS hits; + +CREATE TABLE hits ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + uuid TEXT NOT NULL, + user TEXT NOT NULL +); + diff --git a/templates/template.html b/templates/template.html new file mode 100644 index 0000000..5a7b865 --- /dev/null +++ b/templates/template.html @@ -0,0 +1,18 @@ + + + + Example + + +

QR Code:

+

For URL {{ request.url_root + next_uuid }}

+ +

Last 10 users:

+
  • + {% for hit in hits %} +
      {{ hit['created'] }}, {{ hit['user'] }}
    + {% endfor %} +
  • + + + diff --git a/templates/thanks.html b/templates/thanks.html new file mode 100644 index 0000000..d7edfbf --- /dev/null +++ b/templates/thanks.html @@ -0,0 +1,23 @@ + + + + Example + + + {% if error %} +

    Error

    +

    Code has been used already.

    + {% else %} +

    Thanks

    +

    At {{ time }},

    +

    Registered (entrance? exit?) for user {{ user }} .

    +

    Last 10 registers for {{ user }}:

    +
  • + {% for hit in hits %} +
      {{ hit['created'] }}
    + {% endfor %} +
  • + {% endif %} + + +