commit 73ef9bcfaa0296f1c1e0e4e254d2032a68e8093b Author: Jorge E. Gomez Date: Thu Jul 28 01:42:30 2022 -0500 Initial version 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 0000000..ad3d7ab Binary files /dev/null and b/__pycache__/main.cpython-310.pyc differ diff --git a/database.db b/database.db new file mode 100644 index 0000000..3b93c22 Binary files /dev/null and b/database.db differ 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 %} + + +