summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLenczu Vex <kuba.lenczowski03@gmail.com>2025-04-01 19:59:35 +0200
committerLenczu Vex <kuba.lenczowski03@gmail.com>2025-04-03 08:53:25 +0200
commit10905ea5ae9945fd5ae7b90a1cdfa083fd3fbdd1 (patch)
tree9bd62e9a980093d53bace42614a54e89b7b35809
parent7b2245a452352d32d1e9d4af8b1dd211ecc6e272 (diff)
Initial app
-rw-r--r--config.toml8
-rw-r--r--main.py122
-rw-r--r--templates/index.html17
-rw-r--r--templates/login.html29
4 files changed, 176 insertions, 0 deletions
diff --git a/config.toml b/config.toml
new file mode 100644
index 0000000..39ab275
--- /dev/null
+++ b/config.toml
@@ -0,0 +1,8 @@
+[database]
+host = "localhost"
+user = ""
+password = ""
+port = 3306
+database = ""
+[session]
+timeout = 14400
diff --git a/main.py b/main.py
index e69de29..8ead667 100644
--- a/main.py
+++ b/main.py
@@ -0,0 +1,122 @@
+from flask import Flask, request, render_template, redirect, make_response
+import mariadb
+import argon2
+import tomllib as toml
+import secrets
+from dataclasses import dataclass
+from threading import Timer
+from time import time
+
+
+@dataclass
+class User:
+ username: str
+ expire: int
+ timer: Timer
+
+
+def createUserSession(username: str) -> str:
+ session_id = secrets.token_urlsafe(16)
+ verifiedUsers.add(username)
+ verifiedSessions[session_id] = User(
+ username, time() + app.config["SESSION_TIMEOUT"], None
+ )
+ timer = Timer(app.config["SESSION_TIMEOUT"], expireSession, args=[session_id])
+ timer.start()
+ verifiedSessions[session_id].timer = timer
+ return session_id
+
+
+def expireSession(session_id: str):
+ if not verifiedSessions.get(session_id):
+ return
+ userSession: User = verifiedSessions[session_id]
+ userSession.timer.cancel()
+ verifiedUsers.remove(userSession.username)
+ del verifiedSessions[session_id]
+
+
+def configurationReader():
+ with open("config.toml", "r") as config_file:
+ config = toml.load(config_file)
+ app.config["DB_HOST"] = config["database"]["host"]
+ app.config["DB_USER"] = config["database"]["user"]
+ app.config["DB_PASSWORD"] = config["database"]["password"]
+ app.config["DB_NAME"] = config["database"]["name"]
+ app.config["DB_PORT"] = config["database"]["port"]
+ app.config["SESSION_TIMEOUT"] = config["session"]["timeout"]
+
+
+verifiedUsers = set()
+verifiedSessions = dict()
+app = Flask(__name__)
+
+configurationReader()
+
+
+@app.route("/")
+def index():
+ if request.cookies.get("lnsession"):
+ session_id = request.cookies.get("lnsession")
+ if verifiedSessions.get(session_id):
+ return render_template("index.html", logged_in=True)
+ return render_template("index.html", logged_in=False)
+
+
+@app.route("/login", methods=["POST"])
+def login():
+ page = request.args.get("page")
+ username = request.form.get("username")
+ password = request.form.get("password")
+ mariadb_connection = mariadb.connect(
+ user=app.config["DB_USER"],
+ password=app.config["DB_PASSWORD"],
+ host=app.config["DB_HOST"],
+ port=app.config["DB_PORT"],
+ database=app.config["DB_NAME"],
+ )
+ cur = mariadb_connection.cursor()
+ cur.execute("SELECT password FROM users WHERE username = ?", (username,))
+ result = cur.fetchone()
+ cur.close()
+ mariadb_connection.close()
+ if result is None:
+ return render_template("login.html", error="Invalid username or password"), 401
+
+ stored_password = result[0]
+ ph = argon2.PasswordHasher()
+ try:
+ ph.verify(stored_password, password)
+ except argon2.exceptions.VerifyMismatchError:
+ return render_template("login.html", error="Invalid username or password"), 401
+ if page is None:
+ page = "/"
+
+ token = createUserSession(username)
+ resp = make_response(redirect(page))
+ resp.set_cookie("lnsession", token, max_age=app.config["SESSION_TIMEOUT"])
+
+ return resp
+
+
+@app.route("/login", methods=["GET"])
+def login_get():
+ return render_template("login.html")
+
+
+@app.route("/logout")
+def logout():
+ session_id = request.cookies.get("lnsession")
+ if verifiedSessions.get(session_id):
+ expireSession(session_id)
+ resp = make_response(redirect("/"))
+ resp.set_cookie("lnsession", "", expires=0)
+ return resp
+
+
+@app.route("/api_logged_in")
+def api_logged_in():
+ session_id = request.cookies.get("lnsession")
+ if verifiedSessions.get(session_id):
+ return {"logged_in": True}
+ return {"logged_in": False}
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..ba3d75f
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,17 @@
+<html>
+
+<head>
+ <title>LenczuNet SSO</title>
+ <meta charset="UTF-8">
+</head>
+
+<body>
+ <h1>Welcome!</h1>
+ {% if loggedin %}
+ <button onclick="window.location.href='/logout'">Logout</button>
+ {% else %}
+ <button onclick="window.location.href='/login'">Login</button>
+ {% endif %}
+</body>
+
+</html> \ No newline at end of file
diff --git a/templates/login.html b/templates/login.html
new file mode 100644
index 0000000..d14856b
--- /dev/null
+++ b/templates/login.html
@@ -0,0 +1,29 @@
+<html lang="en">
+
+<head>
+ <title>Login</title>
+ <meta charset="UTF-8">
+</head>
+
+<body>
+ <h1>LenczuNet SSO</h1>
+ {% if error %}
+ <div style="color: red;">
+ <strong>{{ error }}</strong>
+ </div>
+ {% elif timeout %}
+ <div style="color: red;">
+ <strong>Session timed out. Please log in again.</strong>
+ </div>
+ {% endif %}
+ <form>
+ <label for="username">Username:</label>
+ <input type="text" autocomplete="off" id="username" name="username" required />
+ <br />
+ <label for="password">Password:</label>
+ <input type="password" id="password" name="password" required />
+ <input type="submit" value="Login" />
+ </form>
+</body>
+
+</html> \ No newline at end of file